summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG2
-rw-r--r--Rakefile2
-rwxr-xr-xbin/puppet11
-rwxr-xr-xbin/puppetd6
-rw-r--r--conf/redhat/puppet.spec3
-rw-r--r--documentation/Rakefile58
-rw-r--r--documentation/faq.page258
-rw-r--r--documentation/index.page71
-rw-r--r--documentation/reference/configref.header94
-rw-r--r--documentation/reference/configref.page576
-rw-r--r--documentation/reference/functions.header15
-rw-r--r--documentation/reference/functions.page51
-rw-r--r--documentation/reference/index.page18
-rw-r--r--documentation/reference/reports.header19
-rw-r--r--documentation/reference/reports.page82
-rw-r--r--documentation/reference/typedocs.header7
-rw-r--r--documentation/reference/typedocs.page1775
-rw-r--r--lib/puppet.rb2
-rw-r--r--lib/puppet/defaults.rb (renamed from lib/puppet/configuration.rb)7
-rw-r--r--lib/puppet/dsl.rb22
-rw-r--r--lib/puppet/error.rb2
-rw-r--r--lib/puppet/fact_stores/yaml.rb2
-rw-r--r--lib/puppet/feature/base.rb3
-rw-r--r--lib/puppet/indirector.rb76
-rw-r--r--lib/puppet/metatype/evaluation.rb11
-rw-r--r--lib/puppet/metatype/metaparams.rb4
-rw-r--r--lib/puppet/metatype/providers.rb5
-rw-r--r--lib/puppet/module.rb68
-rw-r--r--lib/puppet/network/client/master.rb9
-rw-r--r--lib/puppet/network/handler.rb6
-rw-r--r--lib/puppet/network/handler/configuration.rb304
-rwxr-xr-xlib/puppet/network/handler/facts.rb2
-rwxr-xr-xlib/puppet/network/handler/fileserver.rb42
-rw-r--r--lib/puppet/network/handler/master.rb209
-rw-r--r--lib/puppet/network/handler/node.rb110
-rwxr-xr-xlib/puppet/network/handler/resource.rb4
-rw-r--r--lib/puppet/node.rb61
-rw-r--r--lib/puppet/node/configuration.rb123
-rw-r--r--lib/puppet/node_source/ldap.rb40
-rw-r--r--lib/puppet/parser/ast/astarray.rb82
-rw-r--r--lib/puppet/parser/ast/branch.rb10
-rw-r--r--lib/puppet/parser/ast/caseopt.rb11
-rw-r--r--lib/puppet/parser/ast/casestatement.rb12
-rw-r--r--lib/puppet/parser/ast/collection.rb4
-rw-r--r--lib/puppet/parser/ast/collexpr.rb2
-rw-r--r--lib/puppet/parser/ast/component.rb4
-rw-r--r--lib/puppet/parser/ast/definition.rb214
-rw-r--r--lib/puppet/parser/ast/else.rb10
-rw-r--r--lib/puppet/parser/ast/function.rb2
-rw-r--r--lib/puppet/parser/ast/hostclass.rb41
-rw-r--r--lib/puppet/parser/ast/ifstatement.rb13
-rw-r--r--lib/puppet/parser/ast/leaf.rb8
-rw-r--r--lib/puppet/parser/ast/node.rb23
-rw-r--r--lib/puppet/parser/ast/resource.rb75
-rw-r--r--lib/puppet/parser/ast/resource_defaults.rb (renamed from lib/puppet/parser/ast/resourcedefaults.rb)19
-rw-r--r--lib/puppet/parser/ast/resource_override.rb (renamed from lib/puppet/parser/ast/resourceoverride.rb)8
-rw-r--r--lib/puppet/parser/ast/resource_reference.rb66
-rw-r--r--lib/puppet/parser/ast/resourcedef.rb222
-rw-r--r--lib/puppet/parser/ast/resourceparam.rb14
-rw-r--r--lib/puppet/parser/ast/resourceref.rb58
-rw-r--r--lib/puppet/parser/ast/selector.rb10
-rw-r--r--lib/puppet/parser/ast/tag.rb2
-rw-r--r--lib/puppet/parser/ast/vardef.rb14
-rw-r--r--lib/puppet/parser/collector.rb17
-rw-r--r--lib/puppet/parser/compile.rb498
-rw-r--r--lib/puppet/parser/configuration.rb133
-rw-r--r--lib/puppet/parser/functions.rb13
-rw-r--r--lib/puppet/parser/grammar.ra14
-rw-r--r--lib/puppet/parser/interpreter.rb545
-rw-r--r--lib/puppet/parser/parser.rb18
-rw-r--r--lib/puppet/parser/parser_support.rb47
-rw-r--r--lib/puppet/parser/resource.rb373
-rw-r--r--lib/puppet/parser/resource/reference.rb31
-rw-r--r--lib/puppet/parser/scope.rb333
-rw-r--r--lib/puppet/parser/templatewrapper.rb8
-rw-r--r--lib/puppet/propertychange.rb2
-rw-r--r--lib/puppet/rails/host.rb14
-rw-r--r--lib/puppet/rails/resource.rb8
-rw-r--r--lib/puppet/sslcertificates/ca.rb42
-rw-r--r--lib/puppet/type/component.rb24
-rw-r--r--lib/puppet/type/pfile.rb3
-rwxr-xr-xlib/puppet/type/pfile/content.rb15
-rwxr-xr-xlib/puppet/type/pfile/source.rb39
-rw-r--r--lib/puppet/util.rb2
-rw-r--r--lib/puppet/util/autoload.rb27
-rw-r--r--lib/puppet/util/config.rb419
-rw-r--r--lib/puppet/util/diff.rb71
-rw-r--r--lib/puppet/util/methodhelper.rb2
-rw-r--r--spec/Rakefile10
-rwxr-xr-xspec/bin/spec3
-rw-r--r--spec/lib/spec.rb (renamed from test/lib/spec.rb)13
-rw-r--r--spec/lib/spec/dsl.rb11
-rw-r--r--spec/lib/spec/dsl/behaviour.rb220
-rw-r--r--spec/lib/spec/dsl/behaviour_callbacks.rb82
-rw-r--r--spec/lib/spec/dsl/behaviour_eval.rb231
-rwxr-xr-xspec/lib/spec/dsl/behaviour_factory.rb42
-rw-r--r--spec/lib/spec/dsl/composite_proc_builder.rb33
-rwxr-xr-xspec/lib/spec/dsl/configuration.rb135
-rwxr-xr-xspec/lib/spec/dsl/description.rb76
-rw-r--r--spec/lib/spec/dsl/errors.rb9
-rw-r--r--spec/lib/spec/dsl/example.rb135
-rwxr-xr-xspec/lib/spec/dsl/example_matcher.rb40
-rw-r--r--spec/lib/spec/dsl/example_should_raise_handler.rb (renamed from test/lib/spec/runner/spec_should_raise_handler.rb)8
-rw-r--r--spec/lib/spec/expectations.rb (renamed from test/lib/spec/expectations.rb)5
-rw-r--r--spec/lib/spec/expectations/differs/default.rb (renamed from test/lib/spec/expectations/differs/default.rb)1
-rw-r--r--spec/lib/spec/expectations/errors.rb (renamed from test/lib/spec/expectations/errors.rb)0
-rw-r--r--spec/lib/spec/expectations/extensions.rb (renamed from test/lib/spec/expectations/extensions.rb)1
-rw-r--r--spec/lib/spec/expectations/extensions/object.rb66
-rw-r--r--spec/lib/spec/expectations/extensions/string_and_symbol.rb (renamed from test/lib/spec/expectations/extensions/string_and_symbol.rb)4
-rw-r--r--spec/lib/spec/expectations/handler.rb43
-rwxr-xr-xspec/lib/spec/extensions.rb1
-rwxr-xr-xspec/lib/spec/extensions/object.rb6
-rw-r--r--spec/lib/spec/matchers.rb (renamed from test/lib/spec/matchers.rb)66
-rw-r--r--spec/lib/spec/matchers/be.rb (renamed from test/lib/spec/matchers/be.rb)67
-rw-r--r--spec/lib/spec/matchers/be_close.rb (renamed from test/lib/spec/matchers/be_close.rb)6
-rw-r--r--spec/lib/spec/matchers/change.rb (renamed from test/lib/spec/matchers/change.rb)0
-rw-r--r--spec/lib/spec/matchers/eql.rb (renamed from test/lib/spec/matchers/eql.rb)2
-rw-r--r--spec/lib/spec/matchers/equal.rb (renamed from test/lib/spec/matchers/equal.rb)2
-rw-r--r--spec/lib/spec/matchers/has.rb (renamed from test/lib/spec/matchers/has.rb)0
-rw-r--r--spec/lib/spec/matchers/have.rb (renamed from test/lib/spec/matchers/have.rb)32
-rw-r--r--spec/lib/spec/matchers/include.rb70
-rw-r--r--spec/lib/spec/matchers/match.rb (renamed from test/lib/spec/matchers/match.rb)0
-rwxr-xr-xspec/lib/spec/matchers/operator_matcher.rb72
-rw-r--r--spec/lib/spec/matchers/raise_error.rb (renamed from test/lib/spec/matchers/raise_error.rb)13
-rw-r--r--spec/lib/spec/matchers/respond_to.rb45
-rw-r--r--spec/lib/spec/matchers/satisfy.rb (renamed from test/lib/spec/matchers/satisfy.rb)0
-rw-r--r--spec/lib/spec/matchers/throw_symbol.rb (renamed from test/lib/spec/matchers/throw_symbol.rb)5
-rw-r--r--spec/lib/spec/mocks.rb (renamed from test/lib/spec/mocks.rb)46
-rw-r--r--spec/lib/spec/mocks/argument_constraint_matchers.rb27
-rw-r--r--spec/lib/spec/mocks/argument_expectation.rb (renamed from test/lib/spec/mocks/argument_expectation.rb)65
-rw-r--r--spec/lib/spec/mocks/error_generator.rb (renamed from test/lib/spec/mocks/error_generator.rb)7
-rw-r--r--spec/lib/spec/mocks/errors.rb (renamed from test/lib/spec/mocks/errors.rb)0
-rw-r--r--spec/lib/spec/mocks/extensions/object.rb (renamed from test/lib/spec/mocks/extensions/object.rb)0
-rw-r--r--spec/lib/spec/mocks/message_expectation.rb (renamed from test/lib/spec/mocks/message_expectation.rb)13
-rw-r--r--spec/lib/spec/mocks/methods.rb39
-rw-r--r--spec/lib/spec/mocks/mock.rb (renamed from test/lib/spec/mocks/mock.rb)11
-rw-r--r--spec/lib/spec/mocks/order_group.rb (renamed from test/lib/spec/mocks/order_group.rb)0
-rw-r--r--spec/lib/spec/mocks/proxy.rb (renamed from test/lib/spec/mocks/mock_handler.rb)33
-rw-r--r--spec/lib/spec/mocks/space.rb28
-rw-r--r--spec/lib/spec/mocks/spec_methods.rb30
-rw-r--r--spec/lib/spec/rake/spectask.rb217
-rw-r--r--spec/lib/spec/rake/verify_rcov.rb (renamed from test/lib/spec/rake/verify_rcov.rb)9
-rw-r--r--spec/lib/spec/runner.rb165
-rw-r--r--spec/lib/spec/runner/backtrace_tweaker.rb (renamed from test/lib/spec/runner/backtrace_tweaker.rb)6
-rw-r--r--spec/lib/spec/runner/behaviour_runner.rb123
-rw-r--r--spec/lib/spec/runner/command_line.rb22
-rw-r--r--spec/lib/spec/runner/drb_command_line.rb (renamed from test/lib/spec/runner/drb_command_line.rb)6
-rw-r--r--spec/lib/spec/runner/extensions/kernel.rb50
-rw-r--r--spec/lib/spec/runner/extensions/object.rb (renamed from test/lib/spec/runner/extensions/object.rb)0
-rw-r--r--spec/lib/spec/runner/formatter.rb (renamed from test/lib/spec/runner/formatter.rb)4
-rw-r--r--spec/lib/spec/runner/formatter/base_formatter.rb76
-rw-r--r--spec/lib/spec/runner/formatter/base_text_formatter.rb130
-rw-r--r--spec/lib/spec/runner/formatter/failing_behaviours_formatter.rb29
-rw-r--r--spec/lib/spec/runner/formatter/failing_examples_formatter.rb22
-rw-r--r--spec/lib/spec/runner/formatter/html_formatter.rb323
-rw-r--r--spec/lib/spec/runner/formatter/progress_bar_formatter.rb31
-rw-r--r--spec/lib/spec/runner/formatter/rdoc_formatter.rb24
-rw-r--r--spec/lib/spec/runner/formatter/snippet_extractor.rb52
-rw-r--r--spec/lib/spec/runner/formatter/specdoc_formatter.rb29
-rw-r--r--spec/lib/spec/runner/heckle_runner.rb (renamed from test/lib/spec/runner/heckle_runner.rb)15
-rw-r--r--spec/lib/spec/runner/heckle_runner_unsupported.rb (renamed from test/lib/spec/runner/heckle_runner_win.rb)20
-rw-r--r--spec/lib/spec/runner/option_parser.rb227
-rw-r--r--spec/lib/spec/runner/options.rb175
-rw-r--r--spec/lib/spec/runner/reporter.rb125
-rw-r--r--spec/lib/spec/runner/spec_parser.rb50
-rwxr-xr-xspec/lib/spec/test_case_adapter.rb10
-rw-r--r--spec/lib/spec/translator.rb114
-rw-r--r--spec/lib/spec/version.rb23
-rw-r--r--spec/plugins/mock_frameworks/flexmock.rb23
-rw-r--r--spec/plugins/mock_frameworks/mocha.rb19
-rw-r--r--spec/plugins/mock_frameworks/rr.rb21
-rw-r--r--spec/plugins/mock_frameworks/rspec.rb18
-rw-r--r--spec/spec.opts5
-rw-r--r--spec/spec_helper.rb13
-rwxr-xr-xspec/unit/node/configuration.rb135
-rwxr-xr-xspec/unit/other/modules.rb156
-rwxr-xr-xspec/unit/other/node.rb101
-rwxr-xr-xspec/unit/parser/compile.rb52
-rwxr-xr-xspec/unit/parser/interpreter.rb194
-rwxr-xr-xspec/unit/parser/resource.rb39
-rwxr-xr-xspec/unit/parser/resource/reference.rb66
-rwxr-xr-xspec/unit/util/config.rb408
-rwxr-xr-xtest/certmgr/certmgr.rb14
-rwxr-xr-xtest/language/ast.rb49
-rwxr-xr-xtest/language/ast/definition.rb (renamed from test/language/ast/component.rb)116
-rwxr-xr-xtest/language/ast/hostclass.rb77
-rwxr-xr-xtest/language/ast/resource.rb59
-rwxr-xr-xtest/language/ast/resource_reference.rb (renamed from test/language/ast/resourceref.rb)26
-rwxr-xr-xtest/language/collector.rb33
-rwxr-xr-xtest/language/compile.rb669
-rwxr-xr-xtest/language/configuration.rb131
-rwxr-xr-xtest/language/functions.rb105
-rwxr-xr-xtest/language/interpreter.rb537
-rwxr-xr-xtest/language/parser.rb45
-rwxr-xr-xtest/language/resource.rb573
-rwxr-xr-xtest/language/scope.rb529
-rwxr-xr-xtest/language/snippets.rb19
-rwxr-xr-xtest/lib/puppettest.rb2
-rw-r--r--test/lib/puppettest/parsertesting.rb40
-rw-r--r--test/lib/puppettest/railstesting.rb8
-rw-r--r--test/lib/puppettest/resourcetesting.rb39
-rw-r--r--test/lib/spec/callback.rb11
-rw-r--r--test/lib/spec/callback/callback_container.rb60
-rw-r--r--test/lib/spec/callback/extensions/module.rb24
-rw-r--r--test/lib/spec/callback/extensions/object.rb37
-rw-r--r--test/lib/spec/deprecated.rb3
-rw-r--r--test/lib/spec/expectations/extensions/object.rb109
-rw-r--r--test/lib/spec/expectations/extensions/proc.rb57
-rw-r--r--test/lib/spec/expectations/handler.rb47
-rw-r--r--test/lib/spec/expectations/should.rb5
-rwxr-xr-xtest/lib/spec/expectations/should/base.rb64
-rw-r--r--test/lib/spec/expectations/should/change.rb69
-rw-r--r--test/lib/spec/expectations/should/have.rb128
-rwxr-xr-xtest/lib/spec/expectations/should/not.rb74
-rwxr-xr-xtest/lib/spec/expectations/should/should.rb81
-rw-r--r--test/lib/spec/expectations/sugar.rb47
-rw-r--r--test/lib/spec/matchers/include.rb50
-rw-r--r--test/lib/spec/matchers/respond_to.rb35
-rw-r--r--test/lib/spec/mocks/methods.rb40
-rw-r--r--test/lib/spec/rake/spectask.rb173
-rw-r--r--test/lib/spec/runner.rb132
-rw-r--r--test/lib/spec/runner/command_line.rb34
-rw-r--r--test/lib/spec/runner/context.rb154
-rw-r--r--test/lib/spec/runner/context_eval.rb142
-rw-r--r--test/lib/spec/runner/context_runner.rb55
-rw-r--r--test/lib/spec/runner/execution_context.rb17
-rw-r--r--test/lib/spec/runner/extensions/kernel.rb17
-rw-r--r--test/lib/spec/runner/formatter/base_text_formatter.rb118
-rw-r--r--test/lib/spec/runner/formatter/html_formatter.rb219
-rw-r--r--test/lib/spec/runner/formatter/progress_bar_formatter.rb27
-rw-r--r--test/lib/spec/runner/formatter/rdoc_formatter.rb22
-rw-r--r--test/lib/spec/runner/formatter/specdoc_formatter.rb23
-rw-r--r--test/lib/spec/runner/option_parser.rb224
-rw-r--r--test/lib/spec/runner/reporter.rb105
-rwxr-xr-xtest/lib/spec/runner/spec_matcher.rb25
-rw-r--r--test/lib/spec/runner/spec_parser.rb41
-rw-r--r--test/lib/spec/runner/specification.rb114
-rw-r--r--test/lib/spec/translator.rb87
-rw-r--r--test/lib/spec/version.rb30
-rwxr-xr-xtest/network/client/client.rb10
-rwxr-xr-xtest/network/client/master.rb64
-rwxr-xr-xtest/network/handler/configuration.rb191
-rwxr-xr-xtest/network/handler/master.rb207
-rwxr-xr-xtest/network/handler/node.rb83
-rwxr-xr-xtest/network/handler/resource.rb2
-rwxr-xr-xtest/network/server/webrick.rb9
-rwxr-xr-xtest/network/xmlrpc/processor.rb2
-rwxr-xr-xtest/puppet/defaults.rb26
-rwxr-xr-xtest/puppet/modules.rb59
-rwxr-xr-xtest/rails/ast.rb3
-rwxr-xr-xtest/rails/collection.rb56
-rwxr-xr-xtest/rails/configuration.rb (renamed from test/rails/interpreter.rb)45
-rwxr-xr-xtest/rails/host.rb32
-rwxr-xr-xtest/rails/railsparameter.rb4
-rwxr-xr-xtest/rails/railsresource.rb8
-rwxr-xr-xtest/ral/manager/type.rb23
-rwxr-xr-xtest/ral/types/cron.rb1
-rwxr-xr-xtest/tagging/tagging.rb170
-rwxr-xr-xtest/util/autoload.rb27
-rwxr-xr-xtest/util/config.rb164
260 files changed, 9317 insertions, 10603 deletions
diff --git a/CHANGELOG b/CHANGELOG
index 27aa0fa5e..2e9fd1b34 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,6 @@
0.23.2 (misspiggy)
+ Fixed CA race condition. (#693)
+
Added shortname support to config.rb and refactored addargs
Fixed the problem in cron jobs where environment settings
diff --git a/Rakefile b/Rakefile
index d560b85b9..6b0f6a827 100644
--- a/Rakefile
+++ b/Rakefile
@@ -137,5 +137,3 @@ task :dailyclean do
File.unlink(file)
end
end
-
-# $Id$
diff --git a/bin/puppet b/bin/puppet
index 241bac785..36f0fcd62 100755
--- a/bin/puppet
+++ b/bin/puppet
@@ -134,6 +134,11 @@ end
Puppet.parse_config
+# If noop is set, then also enable diffs
+if Puppet[:noop]
+ Puppet[:show_diff] = true
+end
+
unless logset
Puppet::Util::Log.newdestination(:console)
end
@@ -192,7 +197,11 @@ begin
client.getconfig
client.apply
rescue => detail
- $stderr.puts detail
+ if detail.is_a?(XMLRPC::FaultException)
+ $stderr.puts detail.message
+ else
+ $stderr.puts detail
+ end
if Puppet[:trace]
puts detail.backtrace
end
diff --git a/bin/puppetd b/bin/puppetd
index 848a05b20..a03ed8f10 100755
--- a/bin/puppetd
+++ b/bin/puppetd
@@ -219,6 +219,7 @@ begin
Puppet.config.handlearg("--ignorecache")
Puppet.config.handlearg("--no-usecacheonfailure")
Puppet.config.handlearg("--no-splay")
+ Puppet.config.handlearg("--show_diff")
options[:onetime] = true
options[:waitforcert] = 0
unless Puppet::Util::Log.level == :debug
@@ -278,6 +279,11 @@ Puppet.parse_config
Puppet.genconfig
Puppet.genmanifest
+# If noop is set, then also enable diffs
+if Puppet[:noop]
+ Puppet[:show_diff] = true
+end
+
# Default to daemonizing, but if verbose or debug is specified,
# default to staying in the foreground.
unless options.include?(:daemonize)
diff --git a/conf/redhat/puppet.spec b/conf/redhat/puppet.spec
index 038991776..98a85e5e3 100644
--- a/conf/redhat/puppet.spec
+++ b/conf/redhat/puppet.spec
@@ -148,6 +148,9 @@ fi
rm -rf %{buildroot}
%changelog
+* Wed Aug 22 2007 David Lutterkort <dlutter@redhat.com> - 0.23.2-1
+- New version
+
* Thu Jul 26 2007 David Lutterkort <dlutter@redhat.com> - 0.23.1-1
- Remove old config files
diff --git a/documentation/Rakefile b/documentation/Rakefile
deleted file mode 100644
index 6bfe4d239..000000000
--- a/documentation/Rakefile
+++ /dev/null
@@ -1,58 +0,0 @@
-# vim: syntax=ruby
-
-require 'bluecloth'
-
-htmlfiles = []
-
-CLEAN = []
-
-FileList['**/*.page'].each do |src|
- name = src.sub(".page", ".html")
- htmlfiles << name
- CLEAN << name
- file name => [src, "Rakefile"] do
- File.open(name, "w") do |f|
- text = File.read(src).sub(/\A^---[^-]+^---$/, '')
- f.puts BlueCloth.new( text ).to_html
- end
- end
-end
-
-task :clean do
- CLEAN.each do |file|
- if FileTest.directory?(file)
- sh %{rm -rf #{file}}
- elsif FileTest.exists?(file)
- File.unlink(file)
- end
- end
-end
-
-task :html => htmlfiles
-
-task :default => :html
-
-docs = %w{configref typedocs reports functions}
-
-docs.each do |doc|
- task doc do
- docs = %x{puppetdoc --mode #{doc}}
-
- header = "reference/%s.header" % doc
- if FileTest.exists?(header)
- headertext = File.read(header)
- else
- headertext = ""
- end
-
- file = "reference/%s.page" % doc
-
- puts "Creating %s" % file
- File.open(file, "w") do |f|
- f.puts headertext
- f.puts docs
- end
- end
-end
-
-task :docs => docs
diff --git a/documentation/faq.page b/documentation/faq.page
deleted file mode 100644
index 83a83dca6..000000000
--- a/documentation/faq.page
+++ /dev/null
@@ -1,258 +0,0 @@
----
-inMenu: true
-title: FAQ
-orderInfo: 2
----
-What is Puppet?
-----------------
-Puppet is an open-source next-generation server automation tool. It is
-composed of a *declarative* language for expressing system configuration, a
-*client* and *server* for distributing it, and a *library* for realizing the
-configuration.
-
-The primary design goal of Puppet is that it have an expressive enough
-language backed by a powerful enough library that you can write your own
-server automation applications in just a few lines of code. With Puppet, you
-can express the configuration of your entire network in one program capable of
-realizing the configuration. The fact that Puppet has open source combined
-with how easily it can be extended means that you can add whatever
-functionality you think is missing and then contribute it back to the main
-project if you desire.
-
-You can learn more about Puppet by reading its [Documentation][].
-
-What license is Puppet released under?
---------------------------------------
-Puppet is open source and is released under the [GNU Public License][].
-
-Why does Puppet exist?
-----------------------
-Luke Kanies, who founded Reductive Labs, has been doing server automation for
-years, and Puppet is the result of his frustration with existing tools. After
-significant effort spent trying to enhance cfengine, plus a stint at a
-commercial server automation vendor, Luke concluded that the only way to get a
-great automation tool was to develop one.
-
-Puppet is actually the result of years of design and prototyping (called Blink
-during its prototype phases), but only in 2005 was a commercial company
-(Reductive Labs) built to be fully dedicated to its creation. If Puppet is
-not the most powerful and most flexible server automation platform available,
-then its goals are not being met.
-
-Why does Puppet have its own language?
---------------------------------------
-This actually is a frequently asked question, and people most often ask why I
-did not choose to use something like XML or YAML as the configuration
-format; otherwise people ask why I didn't just choose to just use Ruby as the
-input language.
-
-The input format for Puppet is not XML or YAML because these are data formats
-developed to be easy for computers to handle. They do not do conditionals
-(although, yes, they support data structures that could be considered
-conditionals), but mostly, they're just horrible human interfaces. While some
-people are comfortable reading and writing them, there's a reason why we use
-web browsers instead of just reading the HTML directly. Also, using XML or
-YAML would limit the ability to make sure the interface is declarative -- one
-process might treat an XML configuration differently from another.
-
-As to just using Ruby as the input format, that unnecessarily ties Puppet to
-Ruby, which is undesirable, and Ruby provides a bit too much functionality.
-For more detail, see the [thread][] on the puppet-dev list.
-
-
-How does Puppet compare to cfengine?
-------------------------------------
-Puppet could be said to be the next-generation cfengine. The overall design
-is heavily influenced by cfengine, but the language is more powerful than
-cfengine's and the library is more flexible. In addition, Puppet's client and
-server use standard protocols like XMLRPC and are easy to enhance with new
-functionality, so they are well-positioned to become the platform for the
-network applications of the future, while cfengine's client and server rely
-entirely on cfengine-specific protocols and are quite difficult to enhance.
-
-See [How Puppet Compares to Cfengine][] for more information.
-
-How does Puppet compare to available commercial products?
----------------------------------------------------------
-The primary commercial vendors are BladeLogic and OpsWare. While they both
-have useful product lines, Puppet surpasses them by reframing the entire
-server automation problem -- while the commercial vendors are writing GUI
-applications for you, Reductive Labs is providing a development platform with
-all the features of a great language, like library development, code sharing,
-and the ability to version control your configurations.
-
-Trying to express a complex network configuration entirely through a GUI is an
-exercise in frustration that no one should suffer, but expressing the
-abstraction necessary to share those GUI configurations goes beyond
-frustrating.
-
-Of course, another great difference between Puppet and the commercial products
-is that Puppet is open sourced under the [GNU Public License][]. You can
-[download][] the product, try it out, peruse the source, and make whatever
-modifications you want. You have to have more than 100 servers just to get a
-demo from the commercial vendors, but Puppet is available for testing by any
-company that needs to reduce its server administration costs.
-
-Who would find Puppet useful?
------------------------------
-Any organization that would like to reduce the cost of maintaining its
-computers could benefit from using Puppet. However, because the return on
-investment is linked to multiple factors, like current administrative
-overhead, diversity among existing computers, and cost of downtime, it can be
-difficult for organizations to determine whether they should invest in any
-configuration management tools, much less Puppet. Reductive Labs can always
-be contacted directly at info at reductivelabs.com to help answer this question.
-
-Generally, however, an organization should be using server automation if any
-of the following are true:
-
-* It has high server administration costs
-* It pays a high price for downtime, either because of contracts or
- opportunity cost
-* It has many servers that are essentially either identical or nearly
- identical
-* Flexibility and agility in server configuration are essential
-
-Can Puppet manage workstations?
--------------------------------
-Yes, Puppet can manage any type of machine. We have found that most
-organizations are more concerned with server management than workstation
-management, and frankly, the term 'server' is slightly more aesthetically
-appealing than 'computer', but Puppet would be ideal for organizations with a
-large number of workstations.
-
-Does Puppet run on Windows?
----------------------------
-The short answer is 'not yet'. It will eventually, but Reductive Labs does
-not yet have the development bandwidth to make this work.
-
-What size organizations should use Puppet?
-------------------------------------------
-There is no minimum or maximum organization size that can benefit from Puppet,
-but there are sizes that are more likely to benefit. Organizations with fewer
-than 10-20 servers are unlikely to consider maintaining those servers to be a
-real problem, and thus they can avoid investment in tools even though those
-tools could likely provide savings.
-
-There is no real upper limit to who could benefit from using Puppet.
-Obviously as the server count increases the investment must increase somewhat,
-but with Puppet that increase is not linear.
-
-My servers are all unique; can Puppet still help?
--------------------------------------------------
-All servers are at least somewhat unique -- with different host names and
-different IP addresses -- but very few servers are entirely unique, since
-nearly every one runs a relatively standard operating system. Servers are
-also often very similar to other servers within a single organization -- all
-Solaris servers might have similar security settings, or all web servers might
-have roughly equivalent configurations -- even if they're very different from
-servers in other organizations. Finally, servers are often needlessly unique,
-in that they have been built and managed manually with no attempt at retaining
-appropriate consistency.
-
-Puppet can help both on the side of consistency and uniqueness. Puppet
-can be used to express the consistency that should exist, even if
-that consistency spans arbritrary sets of servers based on any type of data
-like operating system, data center, or physical location. Puppet can also be
-used to handle uniqueness, either by allowing special provision of what makes
-a given host unique or through specifying exceptions to otherwise standard
-classes.
-
-Who is Reductive Labs?
-----------------------
-Reductive Labs is a small, private company focused on reframing the server
-automation problem. Our primary focus is Puppet, but Reductive Labs also
-provides automation consulting, training, and custom development. For more
-information email info at reductivelabs.com.
-
-The [Projects][] page lists our active projects.
-
-How Do I Install Puppet?
-------------------------
-
-The [Installation Guide][] documents the fastest way to start using Puppet.
-
-What is a Manifest?
--------------------
-Because the word *script* implies a procedural one-step-after-another program,
-the word does not apply well to Puppet programs. Thus, we use the word
-*manifest* to describe declarative Puppet programs. Speaking of applying,
-Puppet *applies* a manifest to a server or a network, rather than *executing*
-it.
-
-How Do I Write Manifests?
--------------------------
-The best way is to download Puppet and just start writing. There are multiple
-sets of examples, including the [examples][] used in [unit testing][],
-and the [reference][]will obviously be useful.
-
-How Do I Run Manifests?
------------------------
-Once you have Puppet installed according the the [Installation Guide][],
-just run the ``puppet`` executable against your example:
-
- puppet -v example.pp
-
-How do I contribute?
---------------------
-First join the [Mailing List][] -- there is currently only a development list,
-but as the community grows a user list will be created. You can also join the
-IRC channel ``#puppet`` on irc.freenode.net, where Puppet's developers will be
-hanging out most days (and nights).
-
-The most valuable contribution you can make, though, is to use Puppet and
-submit your feedback, either directly on IRC or through the mailing list, or
-via the [bug database][]. We're always looking for great ideas to incorporate
-into Puppet.
-
-When is the Next Release?
--------------------------
-There are regular feature and release updates on the [Mailing List][],
-and you can always find the latest release in the [download][] directory.
-
-I keep getting "certificates were not trusted". What's wrong?
---------------------------------------------------------------
-Historically this has usually been a problem with the client machine having
-such a different date setting that the certificate is not yet valid.
-
-You can figure the problem out by manually verifying the certificate with
-openssl:
-
- sudo openssl verify -CAfile /etc/puppet/ssl/certs/ca.pem /etc/puppet/ssl/certs/myhostname.domain.com.pem
-
-[Mailing List]: http://mail.madstop.com/mailman/listinfo/puppet-dev
-[Projects]: /projects/
-[Documentation]: documentation/index.html
-[Installation Guide]: documentation/installation.html
-[How Puppet Compares to Cfengine]: documentation/notcfengine.html
-[GNU Public License]: http://www.gnu.org/copyleft/gpl.html
-[examples]: /trac/puppet/browser/trunk/examples/code/
-[unit testing]: http://www.pragmaticprogrammer.com/starter_kit/ut/
-[bug database]: /trac/puppet/report
-[reference]: documentation/typedocs.html
-[download]: /downloads
-[thread]: http://mail.madstop.com/pipermail/puppet-dev/2006-April/000393.html
-
-I'm getting IPv6 errors; what's wrong?
---------------------------------------
-This can apparently happen if Ruby is not compiled with IPv6 support; see the
-[mail thread](http://mail.madstop.com/pipermail/puppet-dev/2006-August/001410.html)
-for more details. The only known solution is to make sure you're running a
-version of Ruby compiled with IPv6 support.
-
-I'm getting ``tlsv1 alert unknown ca`` errors; what's wrong?
-------------------------------------------------------------
-This problem is caused by ``puppetmasterd`` not being able to read its
-ca certificate. This problem might occur up to 0.18.4 but has been
-fixed in 0.19.0. You can probably fix it for versions before 0.19.0 by
-chgrping /etc/puppet/ssl to the puppet group, but ``puppetd`` might
-chgrp it back. Having ``puppetmasterd`` start as the root group should
-fix the problem permanently until you can upgrade.
-
-How do all of these variables, like ``operatingsystem``, get set?
------------------------------------------------------------------
-The variables are all set by [Facter](/projects/facter). You can get
-a full listing of the available variables and their values by running
-``facter`` by itself in a shell.
-
-*$Id$*
diff --git a/documentation/index.page b/documentation/index.page
deleted file mode 100644
index 940951258..000000000
--- a/documentation/index.page
+++ /dev/null
@@ -1,71 +0,0 @@
----
-inMenu: false
-directoryName: Puppet
----
-
-Puppet lets you centrally manage every important aspect of your system using a
-cross-platform specification language that manages all the separate elements
-normally aggregated in different files, like users, cron jobs, and hosts,
-along with obviously discrete elements like packages, services, and files.
-
-Puppet's simple declarative specification language provides powerful classing
-abilities for drawing out the similarities between hosts while allowing them
-to be as specific as necessary, and it handles dependency and prerequisite
-relationships between objects clearly and explicitly. Puppet is written
-entirely in [Ruby](http://www.ruby-lang.org/).
-
-Many general questions about Puppet and Reductive are answered in the
-[FAQ](faq.html), such as "How to get started quickly", "How to contribute",
-and "What is Puppet's License? (GPL)")
-
-You can also often get good support on ``#puppet`` on irc.freenode.net;
-Puppet's primary author, Luke Kanies, is usually online there.
-
-## Relevant Links
-
-* [Documentation](/trac/puppet/wiki/DocumentationStart)
-
- Available documentation on puppet. Including an Introduction, and Language
- & Type Library References.
-
-* [Puppet Recipe Manager](http://prmweb.hezmatt.org/)
-
- A CPAN-like site for sharing and downloading Puppet recipes.
-
-* [Cookbook](/trac/puppet/tags/puppet%2Crecipe)
-
- All of the cookbook recipes on the Puppet wiki.
-
-* [Downloads](/downloads/)
-
- Puppet source code, Packages (RPMs, debs, etc.), and Ruby GEM packages.
-
-* [Source Code](/svn/puppet/)
-
- Puppet Subversion Repository
-
-* [Bug Tracker](https://reductivelabs.com/trac/puppet)
-
- Bug tickets, feature enhancements, and source browsing
-
-* [Configuration Management Blog](http://madstop.com)
-
- A blog Luke Kanies is maintaining about the development process of Puppet.
-
-## Mailing Lists
-
-* [Puppet User](http://mail.madstop.com/mailman/listinfo/puppet-users)
-
- The Puppet users mailing list, for any and all Puppet discussion.
-
-* [Puppet Developer](http://mail.madstop.com/mailman/listinfo/puppet-dev)
-
- The Puppet-dev mailing list, for all public discussions related to the
- development of puppet. All emails generated by the bug tracker are also
- sent to this list.
-
-* [Puppet Commits](http://mail.madstop.com/mailman/listinfo/puppet-commit)
-
- A read-only list that gets a copy of all subversion commits.
-
-*$Id$*
diff --git a/documentation/reference/configref.header b/documentation/reference/configref.header
deleted file mode 100644
index 854eb1d99..000000000
--- a/documentation/reference/configref.header
+++ /dev/null
@@ -1,94 +0,0 @@
----
-inMenu: true
-title: Configuration Reference
-orderInfo: 40
----
-# Puppet Configuration Reference
-
-## Specifying Configuration Parameters
-
-Every Puppet executable (with the exception of ``puppetdoc``) accepts all of
-the arguments below, but not all of the arguments make sense for every executable.
-Each argument has a section listed with it in parentheses; often, that section
-will map to an executable (e.g., ``puppetd``), in which case it probably only
-makes sense for that one executable. If ``puppet`` is listed as the section,
-it is most likely an option that is valid for everyone.
-
-This will not always be the case. I have tried to be as thorough as possible
-in the descriptions of the arguments, so it should be obvious whether an
-argument is appropriate or not.
-
-These arguments can be supplied to the executables either as command-line
-arugments or in the configuration file for the appropriate executable. For
-instance, the command-line invocation below would set the configuration directory
-to /private/puppet
-
- $ puppetd --confdir=/private/puppet
-
-Note that boolean options are turned on and off with a slightly different syntax
-on the command line:
-
- $ puppetd --storeconfigs
-
- $ puppetd --no-storeconfigs
-
-The invocations above will enable and disable, respectively, the storage of
-the client configuration.
-
-As mentioned above, the configuration parameters can also be stored in a
-configuration file located in the configuration directory (`/etc/puppet`
-by default). The file is named for the executable it is intended for, for
-example `/etc/puppetd.conf` is the configuration file for `puppetd`.
-
-The file, which follows INI-style formatting, should contain a bracketed
-heading named for the executable, followed by pairs of parameters with their
-values. Here is an example of a very simple `puppetd.conf` file:
-
- [puppetd]
- confdir = /private/puppet
- storeconfigs = true
-
-Note that boolean parameters must be explicitly specified as `true` or
-`false` as seen above.
-
-If you're starting out with a fresh configuration, you may wish to let
-the executable generate a template configuration file for you by invoking
-the executable in question with the `--genconfig` command. The executable
-will print a template configuration to standard output, which can be
-redirected to a file like so:
-
- $ puppetd --genconfig > /etc/puppet/puppetd.conf
-
-Note that this invocation will "clobber" (throw away) the contents of any
-pre-existing `puppetd.conf` file, so make a backup of your present config
-if it contains valuable information.
-
-Like the `--genconfig` argument, the executables also accept a `--genmanifest`
-argument, which will generate a manifest that can be used to manage all of
-Puppet's directories and files and prints it to standard output. This can
-likewise be redirected to a file:
-
- $ puppetd --genmanifest > /etc/puppet/manifests/site.pp
-
-Puppet can also create user and group accounts for itself (one `puppet` group
-and one `puppet` user) if it is invoked as `root` with the `--mkusers` argument:
-
- $ puppetd --mkusers
-
-## Signals
-
-The `puppetd` and `puppetmasterd` executables catch some signals for special
-handling. Both daemons catch (`SIGHUP`), which forces the server to restart
-tself. Predictably, interrupt and terminate (`SIGINT` and `SIGHUP`) will shut
-down the server, whether it be an instance of `puppetd` or `puppetmasterd`.
-
-Sending the `SIGUSR1` signal to an instance of `puppetd` will cause it to
-immediately begin a new configuration transaction with the server. This
-signal has no effect on `puppetmasterd`.
-
-
-## Configuration Parameter Reference
-
-Below is a list of all documented parameters. Any default values are in ``block type`` at the end of the description.
-
-
diff --git a/documentation/reference/configref.page b/documentation/reference/configref.page
deleted file mode 100644
index d3081fe0c..000000000
--- a/documentation/reference/configref.page
+++ /dev/null
@@ -1,576 +0,0 @@
----
-inMenu: true
-title: Configuration Reference
-orderInfo: 40
----
-# Puppet Configuration Reference
-
-## Specifying Configuration Parameters
-
-Every Puppet executable (with the exception of ``puppetdoc``) accepts all of
-the arguments below, but not all of the arguments make sense for every executable.
-Each argument has a section listed with it in parentheses; often, that section
-will map to an executable (e.g., ``puppetd``), in which case it probably only
-makes sense for that one executable. If ``puppet`` is listed as the section,
-it is most likely an option that is valid for everyone.
-
-This will not always be the case. I have tried to be as thorough as possible
-in the descriptions of the arguments, so it should be obvious whether an
-argument is appropriate or not.
-
-These arguments can be supplied to the executables either as command-line
-arugments or in the configuration file for the appropriate executable. For
-instance, the command-line invocation below would set the configuration directory
-to /private/puppet
-
- $ puppetd --confdir=/private/puppet
-
-Note that boolean options are turned on and off with a slightly different syntax
-on the command line:
-
- $ puppetd --storeconfigs
-
- $ puppetd --no-storeconfigs
-
-The invocations above will enable and disable, respectively, the storage of
-the client configuration.
-
-As mentioned above, the configuration parameters can also be stored in a
-configuration file located in the configuration directory (`/etc/puppet`
-by default). The file is named for the executable it is intended for, for
-example `/etc/puppetd.conf` is the configuration file for `puppetd`.
-
-The file, which follows INI-style formatting, should contain a bracketed
-heading named for the executable, followed by pairs of parameters with their
-values. Here is an example of a very simple `puppetd.conf` file:
-
- [puppetd]
- confdir = /private/puppet
- storeconfigs = true
-
-Note that boolean parameters must be explicitly specified as `true` or
-`false` as seen above.
-
-If you're starting out with a fresh configuration, you may wish to let
-the executable generate a template configuration file for you by invoking
-the executable in question with the `--genconfig` command. The executable
-will print a template configuration to standard output, which can be
-redirected to a file like so:
-
- $ puppetd --genconfig > /etc/puppet/puppetd.conf
-
-Note that this invocation will "clobber" (throw away) the contents of any
-pre-existing `puppetd.conf` file, so make a backup of your present config
-if it contains valuable information.
-
-Like the `--genconfig` argument, the executables also accept a `--genmanifest`
-argument, which will generate a manifest that can be used to manage all of
-Puppet's directories and files and prints it to standard output. This can
-likewise be redirected to a file:
-
- $ puppetd --genmanifest > /etc/puppet/manifests/site.pp
-
-Puppet can also create user and group accounts for itself (one `puppet` group
-and one `puppet` user) if it is invoked as `root` with the `--mkusers` argument:
-
- $ puppetd --mkusers
-
-## Signals
-
-The `puppetd` and `puppetmasterd` executables catch some signals for special
-handling. Both daemons catch (`SIGHUP`), which forces the server to restart
-tself. Predictably, interrupt and terminate (`SIGINT` and `SIGHUP`) will shut
-down the server, whether it be an instance of `puppetd` or `puppetmasterd`.
-
-Sending the `SIGUSR1` signal to an instance of `puppetd` will cause it to
-immediately begin a new configuration transaction with the server. This
-signal has no effect on `puppetmasterd`.
-
-
-## Configuration Parameter Reference
-
-Below is a list of all documented parameters. Any default values are in ``block type`` at the end of the description.
-
-
-#### <a name="authconfig">authconfig</a> (<em>puppet</em>)
-
-The configuration file that defines the rights to the different namespaces and methods. This can be used as a coarse-grained authorization system for both ``puppetd`` and ``puppetmasterd``. ``/etc/puppet/namespaceauth.conf``
-
-#### <a name="autoflush">autoflush</a> (<em>puppet</em>)
-
-Whether log files should always flush to disk.
-
-#### <a name="autosign">autosign</a> (<em>ca</em>)
-
-Whether to enable autosign. Valid values are true (which autosigns any key request, and is a very bad idea), false (which never autosigns any key request), and the path to a file, which uses that configuration file to determine which keys to sign. ``/etc/puppet/autosign.conf``
-
-#### <a name="bucketdir">bucketdir</a> (<em>puppetmasterd</em>)
-
-Where FileBucket files are stored. ``/var/puppet/bucket``
-
-#### <a name="ca_days">ca_days</a> (<em>ca</em>)
-
-How long a certificate should be valid. This parameter is deprecated, use ca_ttl instead
-
-#### <a name="ca_md">ca_md</a> (<em>ca</em>)
-
-The type of hash used in certificates. ``md5``
-
-#### <a name="ca_ttl">ca_ttl</a> (<em>ca</em>)
-
-The default TTL for new certificates; valid values must be an integer, optionally followed by one of the units 'y' (years of 365 days), 'd' (days), 'h' (hours), or 's' (seconds). The unit defaults to seconds. If this parameter is set, ca_days is ignored. Examples are '3600' (one hour) and '1825d', which is the same as '5y' (5 years) ``5y``
-
-#### <a name="cacert">cacert</a> (<em>ca</em>)
-
-The CA certificate. ``/etc/puppet/ssl/ca/ca_crt.pem``
-
-#### <a name="cacrl">cacrl</a> (<em>ca</em>)
-
-The certificate revocation list (CRL) for the CA. Set this to 'none' if you do not want to use a CRL. ``/etc/puppet/ssl/ca/ca_crl.pem``
-
-#### <a name="cadir">cadir</a> (<em>ca</em>)
-
-The root directory for the certificate authority. ``/etc/puppet/ssl/ca``
-
-#### <a name="cakey">cakey</a> (<em>ca</em>)
-
-The CA private key. ``/etc/puppet/ssl/ca/ca_key.pem``
-
-#### <a name="capass">capass</a> (<em>ca</em>)
-
-Where the CA stores the password for the private key ``/etc/puppet/ssl/ca/private/ca.pass``
-
-#### <a name="caprivatedir">caprivatedir</a> (<em>ca</em>)
-
-Where the CA stores private certificate information. ``/etc/puppet/ssl/ca/private``
-
-#### <a name="capub">capub</a> (<em>ca</em>)
-
-The CA public key. ``/etc/puppet/ssl/ca/ca_pub.pem``
-
-#### <a name="casesensitive">casesensitive</a> (<em>puppet</em>)
-
-Whether matching in case statements and selectors should be case-sensitive. Case insensitivity is handled by downcasing all values before comparison.
-
-#### <a name="cert_inventory">cert_inventory</a> (<em>ca</em>)
-
-A Complete listing of all certificates ``/etc/puppet/ssl/ca/inventory.txt``
-
-#### <a name="certdir">certdir</a> (<em>certificates</em>)
-
-The certificate directory. ``/etc/puppet/ssl/certs``
-
-#### <a name="classfile">classfile</a> (<em>puppetd</em>)
-
-The file in which puppetd stores a list of the classes associated with the retrieved configuratiion. Can be loaded in the separate ``puppet`` executable using the ``--loadclasses`` option. ``/etc/puppet/classes.txt``
-
-#### <a name="clientbucketdir">clientbucketdir</a> (<em>filebucket</em>)
-
-Where FileBucket files are stored locally. ``/var/puppet/clientbucket``
-
-#### <a name="color">color</a> (<em>puppet</em>)
-
-Whether to use colors when logging to the console. Valid values are ``ansi`` (equivalent to ``true``), ``html`` (mostly used during testing with TextMate), and ``false``, which produces no color. ``ansi``
-
-#### <a name="confdir">confdir</a> (<em>puppet</em>)
-
-The main Puppet configuration directory. ``/etc/puppet``
-
-#### <a name="config">config</a> (<em>puppetdoc</em>)
-
-The configuration file for puppetdoc. ``/etc/puppet/puppetdoc.conf``
-
-#### <a name="configprint">configprint</a> (<em>puppet</em>)
-
-Print the value of a specific configuration parameter. If a parameter is provided for this, then the value is printed and puppet exits. Comma-separate multiple values. For a list of all values, specify 'all'. This feature is only available in Puppet versions higher than 0.18.4.
-
-#### <a name="configtimeout">configtimeout</a> (<em>puppetd</em>)
-
-How long the client should wait for the configuration to be retrieved before considering it a failure. This can help reduce flapping if too many clients contact the server at one time. ``30``
-
-#### <a name="csrdir">csrdir</a> (<em>ca</em>)
-
-Where the CA stores certificate requests ``/etc/puppet/ssl/ca/requests``
-
-#### <a name="dbadapter">dbadapter</a> (<em>puppetmaster</em>)
-
-The type of database to use. ``sqlite3``
-
-#### <a name="dblocation">dblocation</a> (<em>puppetmaster</em>)
-
-The database cache for client configurations. Used for querying within the language. ``/var/puppet/state/clientconfigs.sqlite3``
-
-#### <a name="dbmigrate">dbmigrate</a> (<em>puppetmaster</em>)
-
-Whether to automatically migrate the database.
-
-#### <a name="dbname">dbname</a> (<em>puppetmaster</em>)
-
-The name of the database to use. ``puppet``
-
-#### <a name="dbpassword">dbpassword</a> (<em>puppetmaster</em>)
-
-The database password for Client caching. Only used when networked databases are used. ``puppet``
-
-#### <a name="dbserver">dbserver</a> (<em>puppetmaster</em>)
-
-The database server for Client caching. Only used when networked databases are used. ``localhost``
-
-#### <a name="dbuser">dbuser</a> (<em>puppetmaster</em>)
-
-The database user for Client caching. Only used when networked databases are used. ``puppet``
-
-#### <a name="downcasefacts">downcasefacts</a> (<em>puppetd</em>)
-
-Whether facts should be made all lowercase when sent to the server.
-
-#### <a name="evaltrace">evaltrace</a> (<em>transaction</em>)
-
-Whether each resource should log when it is being evaluated. This allows you to interactively see exactly what is being done.
-
-#### <a name="external_nodes">external_nodes</a> (<em>puppet</em>)
-
-An external command that can produce node information. The first line of output must be either the parent node or blank, and if there is a second line of output it should be a list of whitespace-separated classes to include on that node. This command makes it straightforward to store your node mapping information in other data sources like databases. For unknown nodes, the commands should exit with an exit code of 1. ``none``
-
-#### <a name="factdest">factdest</a> (<em>puppet</em>)
-
-Where Puppet should store facts that it pulls down from the central server. ``/var/puppet/facts``
-
-#### <a name="factpath">factpath</a> (<em>puppet</em>)
-
-Where Puppet should look for facts. Multiple directories should be colon-separated, like normal PATH variables. ``/var/puppet/facts``
-
-#### <a name="factsignore">factsignore</a> (<em>puppet</em>)
-
-What files to ignore when pulling down facts. ``.svn CVS``
-
-#### <a name="factsource">factsource</a> (<em>puppet</em>)
-
-From where to retrieve facts. The standard Puppet ``file`` type is used for retrieval, so anything that is a valid file source can be used here. ``puppet://puppet/facts``
-
-#### <a name="factsync">factsync</a> (<em>puppet</em>)
-
-Whether facts should be synced with the central server.
-
-#### <a name="fileserverconfig">fileserverconfig</a> (<em>fileserver</em>)
-
-Where the fileserver configuration is stored. ``/etc/puppet/fileserver.conf``
-
-#### <a name="filetimeout">filetimeout</a> (<em>puppet</em>)
-
-The minimum time to wait between checking for updates in configuration files. ``15``
-
-#### <a name="genconfig">genconfig</a> (<em>puppet</em>)
-
-Whether to just print a configuration to stdout and exit. Only makes sense when used interactively. Takes into account arguments specified on the CLI.
-
-#### <a name="genmanifest">genmanifest</a> (<em>puppet</em>)
-
-Whether to just print a manifest to stdout and exit. Only makes sense when used interactively. Takes into account arguments specified on the CLI.
-
-#### <a name="graph">graph</a> (<em>puppet</em>)
-
-Whether to create dot graph files for the different configuration graphs. These dot files can be interpreted by tools like OmniGraffle or dot (which is part of ImageMagick).
-
-#### <a name="graphdir">graphdir</a> (<em>puppet</em>)
-
-Where to store dot-outputted graphs. ``/var/puppet/state/graphs``
-
-#### <a name="group">group</a> (<em>puppetmasterd</em>)
-
-The group puppetmasterd should run as. ``puppet``
-
-#### <a name="hostcert">hostcert</a> (<em>certificates</em>)
-
-Where individual hosts store and look for their certificates. ``/etc/puppet/ssl/certs/culain.madstop.com.pem``
-
-#### <a name="hostprivkey">hostprivkey</a> (<em>certificates</em>)
-
-Where individual hosts store and look for their private key. ``/etc/puppet/ssl/private_keys/culain.madstop.com.pem``
-
-#### <a name="hostpubkey">hostpubkey</a> (<em>certificates</em>)
-
-Where individual hosts store and look for their public key. ``/etc/puppet/ssl/public_keys/culain.madstop.com.pem``
-
-#### <a name="httplog">httplog</a> (<em>puppetd</em>)
-
-Where the puppetd web server logs. ``/var/puppet/log/http.log``
-
-#### <a name="ignoreschedules">ignoreschedules</a> (<em>puppetd</em>)
-
-Boolean; whether puppetd should ignore schedules. This is useful for initial puppetd runs.
-
-#### <a name="keylength">keylength</a> (<em>ca</em>)
-
-The bit length of keys. ``1024``
-
-#### <a name="ldapattrs">ldapattrs</a> (<em>ldap</em>)
-
-The LDAP attributes to use to define Puppet classes. Values should be comma-separated. ``puppetclass``
-
-#### <a name="ldapbase">ldapbase</a> (<em>ldap</em>)
-
-The search base for LDAP searches. It's impossible to provide a meaningful default here, although the LDAP libraries might have one already set. Generally, it should be the 'ou=Hosts' branch under your main directory.
-
-#### <a name="ldapnodes">ldapnodes</a> (<em>ldap</em>)
-
-Whether to search for node configurations in LDAP.
-
-#### <a name="ldapparentattr">ldapparentattr</a> (<em>ldap</em>)
-
-The attribute to use to define the parent node. ``parentnode``
-
-#### <a name="ldappassword">ldappassword</a> (<em>ldap</em>)
-
-The password to use to connect to LDAP.
-
-#### <a name="ldapport">ldapport</a> (<em>ldap</em>)
-
-The LDAP port. Only used if ``ldapnodes`` is enabled. ``389``
-
-#### <a name="ldapserver">ldapserver</a> (<em>ldap</em>)
-
-The LDAP server. Only used if ``ldapnodes`` is enabled. ``ldap``
-
-#### <a name="ldapssl">ldapssl</a> (<em>ldap</em>)
-
-Whether SSL should be used when searching for nodes. Defaults to false because SSL usually requires certificates to be set up on the client side.
-
-#### <a name="ldapstring">ldapstring</a> (<em>ldap</em>)
-
-The search string used to find an LDAP node. ``(&(objectclass=puppetClient)(cn=%s))``
-
-#### <a name="ldaptls">ldaptls</a> (<em>ldap</em>)
-
-Whether TLS should be used when searching for nodes. Defaults to false because TLS usually requires certificates to be set up on the client side.
-
-#### <a name="ldapuser">ldapuser</a> (<em>ldap</em>)
-
-The user to use to connect to LDAP. Must be specified as a full DN.
-
-#### <a name="lexical">lexical</a> (<em>puppet</em>)
-
-Whether to use lexical scoping (vs. dynamic).
-
-#### <a name="listen">listen</a> (<em>puppetd</em>)
-
-Whether puppetd should listen for connections. If this is true, then by default only the ``runner`` server is started, which allows remote authorized and authenticated nodes to connect and trigger ``puppetd`` runs.
-
-#### <a name="localcacert">localcacert</a> (<em>certificates</em>)
-
-Where each client stores the CA certificate. ``/etc/puppet/ssl/certs/ca.pem``
-
-#### <a name="localconfig">localconfig</a> (<em>puppetd</em>)
-
-Where puppetd caches the local configuration. An extension indicating the cache format is added automatically. ``/etc/puppet/localconfig``
-
-#### <a name="logdir">logdir</a> (<em>puppet</em>)
-
-The Puppet log directory. ``/var/puppet/log``
-
-#### <a name="manifest">manifest</a> (<em>puppetmasterd</em>)
-
-The entry-point manifest for puppetmasterd. ``/etc/puppet/manifests/site.pp``
-
-#### <a name="manifestdir">manifestdir</a> (<em>puppetmasterd</em>)
-
-Where puppetmasterd looks for its manifests. ``/etc/puppet/manifests``
-
-#### <a name="masterhttplog">masterhttplog</a> (<em>puppetmasterd</em>)
-
-Where the puppetmasterd web server logs. ``/var/puppet/log/masterhttp.log``
-
-#### <a name="masterlog">masterlog</a> (<em>puppetmasterd</em>)
-
-Where puppetmasterd logs. This is generally not used, since syslog is the default log destination. ``/var/puppet/log/puppetmaster.log``
-
-#### <a name="masterport">masterport</a> (<em>puppetmasterd</em>)
-
-Which port puppetmasterd listens on. ``8140``
-
-#### <a name="mkusers">mkusers</a> (<em>puppet</em>)
-
-Whether to create the necessary user and group that puppetd will run as.
-
-#### <a name="node_name">node_name</a> (<em>puppetmasterd</em>)
-
-How the puppetmaster determines the client's identity and sets the 'hostname' fact for use in the manifest, in particular for determining which 'node' statement applies to the client. Possible values are 'cert' (use the subject's CN in the client's certificate) and 'facter' (use the hostname that the client reported in its facts) ``cert``
-
-#### <a name="noop">noop</a> (<em>puppetd</em>)
-
-Whether puppetd should be run in noop mode.
-
-#### <a name="paramcheck">paramcheck</a> (<em>ast</em>)
-
-Whether to validate parameters during parsing. ``true``
-
-#### <a name="parseonly">parseonly</a> (<em>puppetmasterd</em>)
-
-Just check the syntax of the manifests.
-
-#### <a name="passfile">passfile</a> (<em>certificates</em>)
-
-Where puppetd stores the password for its private key. Generally unused. ``/etc/puppet/ssl/private/password``
-
-#### <a name="path">path</a> (<em>puppet</em>)
-
-The shell search path. Defaults to whatever is inherited from the parent process. ``none``
-
-#### <a name="plugindest">plugindest</a> (<em>puppet</em>)
-
-Where Puppet should store plugins that it pulls down from the central server. ``/var/puppet/plugins``
-
-#### <a name="pluginpath">pluginpath</a> (<em>puppet</em>)
-
-Where Puppet should look for plugins. Multiple directories should be colon-separated, like normal PATH variables. ``/var/puppet/plugins``
-
-#### <a name="pluginsignore">pluginsignore</a> (<em>puppet</em>)
-
-What files to ignore when pulling down plugins. ``.svn CVS``
-
-#### <a name="pluginsource">pluginsource</a> (<em>puppet</em>)
-
-From where to retrieve plugins. The standard Puppet ``file`` type is used for retrieval, so anything that is a valid file source can be used here. ``puppet://puppet/plugins``
-
-#### <a name="pluginsync">pluginsync</a> (<em>puppet</em>)
-
-Whether plugins should be synced with the central server.
-
-#### <a name="privatedir">privatedir</a> (<em>certificates</em>)
-
-Where the client stores private certificate information. ``/etc/puppet/ssl/private``
-
-#### <a name="privatekeydir">privatekeydir</a> (<em>certificates</em>)
-
-The private key directory. ``/etc/puppet/ssl/private_keys``
-
-#### <a name="publickeydir">publickeydir</a> (<em>certificates</em>)
-
-The public key directory. ``/etc/puppet/ssl/public_keys``
-
-#### <a name="puppetdlockfile">puppetdlockfile</a> (<em>puppetd</em>)
-
-A lock file to temporarily stop puppetd from doing anything. ``/var/puppet/state/puppetdlock``
-
-#### <a name="puppetdlog">puppetdlog</a> (<em>puppetd</em>)
-
-The log file for puppetd. This is generally not used. ``/var/puppet/log/puppetd.log``
-
-#### <a name="puppetport">puppetport</a> (<em>puppetd</em>)
-
-Which port puppetd listens on. ``8139``
-
-#### <a name="railslog">railslog</a> (<em>puppetmaster</em>)
-
-Where Rails-specific logs are sent ``/var/puppet/log/rails.log``
-
-#### <a name="report">report</a> (<em>puppetd</em>)
-
-Whether to send reports after every transaction.
-
-#### <a name="reportdir">reportdir</a> (<em>reporting</em>)
-
-The directory in which to store reports received from the client. Each client gets a separate subdirectory. ``/var/puppet/reports``
-
-#### <a name="reports">reports</a> (<em>reporting</em>)
-
-The list of reports to generate. All reports are looked for in puppet/reports/<name>.rb, and multiple report names should be comma-separated (whitespace is okay). ``store``
-
-#### <a name="reportserver">reportserver</a> (<em>puppetd</em>)
-
-The server to which to send transaction reports. ``puppet``
-
-#### <a name="req_bits">req_bits</a> (<em>ca</em>)
-
-The bit length of the certificates. ``2048``
-
-#### <a name="rrddir">rrddir</a> (<em>metrics</em>)
-
-The directory where RRD database files are stored. Directories for each reporting host will be created under this directory. ``/var/puppet/rrd``
-
-#### <a name="rrdgraph">rrdgraph</a> (<em>metrics</em>)
-
-Whether RRD information should be graphed.
-
-#### <a name="rrdinterval">rrdinterval</a> (<em>metrics</em>)
-
-How often RRD should expect data. This should match how often the hosts report back to the server. ``1800``
-
-#### <a name="rundir">rundir</a> (<em>puppet</em>)
-
-Where Puppet PID files are kept. ``/var/run/puppet``
-
-#### <a name="runinterval">runinterval</a> (<em>puppetd</em>)
-
-How often puppetd applies the client configuration; in seconds ``1800``
-
-#### <a name="serial">serial</a> (<em>ca</em>)
-
-Where the serial number for certificates is stored. ``/etc/puppet/ssl/ca/serial``
-
-#### <a name="server">server</a> (<em>puppetd</em>)
-
-The server to which server puppetd should connect ``puppet``
-
-#### <a name="setpidfile">setpidfile</a> (<em>puppet</em>)
-
-Whether to store a PID file for the daemon. ``true``
-
-#### <a name="signeddir">signeddir</a> (<em>ca</em>)
-
-Where the CA stores signed certificates. ``/etc/puppet/ssl/ca/signed``
-
-#### <a name="ssldir">ssldir</a> (<em>puppet</em>)
-
-Where SSL certificates are kept. ``/etc/puppet/ssl``
-
-#### <a name="statedir">statedir</a> (<em>puppet</em>)
-
-The directory where Puppet state is stored. Generally, this directory can be removed without causing harm (although it might result in spurious service restarts). ``/var/puppet/state``
-
-#### <a name="statefile">statefile</a> (<em>puppet</em>)
-
-Where puppetd and puppetmasterd store state associated with the running configuration. In the case of puppetmasterd, this file reflects the state discovered through interacting with clients. ``/var/puppet/state/state.yaml``
-
-#### <a name="storeconfigs">storeconfigs</a> (<em>puppetmaster</em>)
-
-Whether to store each client's configuration. This requires ActiveRecord from Ruby on Rails.
-
-#### <a name="syslogfacility">syslogfacility</a> (<em>puppet</em>)
-
-What syslog facility to use when logging to syslog. Syslog has a fixed list of valid facilities, and you must choose one of those; you cannot just make one up. ``daemon``
-
-#### <a name="tags">tags</a> (<em>transaction</em>)
-
-Tags to use to find resources. If this is set, then only resources tagged with the specified tags will be applied. Values must be comma-separated.
-
-#### <a name="templatedir">templatedir</a> (<em>puppet</em>)
-
-Where Puppet looks for template files. ``/var/puppet/templates``
-
-#### <a name="trace">trace</a> (<em>puppet</em>)
-
-Whether to print stack traces on some errors
-
-#### <a name="typecheck">typecheck</a> (<em>ast</em>)
-
-Whether to validate types during parsing. ``true``
-
-#### <a name="usecacheonfailure">usecacheonfailure</a> (<em>puppetd</em>)
-
-Whether to use the cached configuration when the remote configuration will not compile. This option is useful for testing new configurations, where you want to fix the broken configuration rather than reverting to a known-good one. ``true``
-
-#### <a name="user">user</a> (<em>puppetmasterd</em>)
-
-The user puppetmasterd should run as. ``puppet``
-
-#### <a name="vardir">vardir</a> (<em>puppet</em>)
-
-Where Puppet stores dynamic and growing data. ``/var/puppet``
-
-
-
-----------------
-
-
-*This page autogenerated on Fri Jan 26 16:40:43 CST 2007*
diff --git a/documentation/reference/functions.header b/documentation/reference/functions.header
deleted file mode 100644
index 9c58cd36a..000000000
--- a/documentation/reference/functions.header
+++ /dev/null
@@ -1,15 +0,0 @@
----
-inMenu: true
-title: Function Reference
-orderInfo: 40
----
-
-There are two types of functions in Puppet: Statements and rvalues.
-Statements stand on their own and do not return arguments; they are used for
-performing stand-alone work like importing. Rvalues return values and can
-only be used in a statement requiring a value, such as an assignment or a case
-statement.
-
-Here are the functions available in Puppet:
-
-
diff --git a/documentation/reference/functions.page b/documentation/reference/functions.page
deleted file mode 100644
index ad2c03f5a..000000000
--- a/documentation/reference/functions.page
+++ /dev/null
@@ -1,51 +0,0 @@
----
-inMenu: true
-title: Function Reference
-orderInfo: 40
----
-
-There are two types of functions in Puppet: Statements and rvalues.
-Statements stand on their own and do not return arguments; they are used for
-performing stand-alone work like importing. Rvalues return values and can
-only be used in a statement requiring a value, such as an assignment or a case
-statement.
-
-Here are the functions available in Puppet:
-
-
-* **alert** (*statement*): Log a message on the server at level alert.
-
-* **crit** (*statement*): Log a message on the server at level crit.
-
-* **debug** (*statement*): Log a message on the server at level debug.
-
-* **defined** (*rvalue*): Determine whether a given type is defined, either as a native type or a defined type, or whether a resource has been specified. If you are checking with a resource is defined, use the normal resource reference syntax, e.g., ``File['/etc/passwd']``.
-
-* **emerg** (*statement*): Log a message on the server at level emerg.
-
-* **err** (*statement*): Log a message on the server at level err.
-
-* **fail** (*statement*): Fail with a parse error.
-
-* **include** (*statement*): Evaluate one or more classes.
-
-* **info** (*statement*): Log a message on the server at level info.
-
-* **notice** (*statement*): Log a message on the server at level notice.
-
-* **realize** (*statement*): Make a virtual object real. This is useful when you want to know the name of the virtual object and don't want to bother with a full collection. It is slightly faster than a collection, and, of course, is a bit shorter. You must pass the object using a reference; e.g.: ``realize User[luke]``.
-
-* **tag** (*statement*): Add the specified tags to the containing class or definition. All contained objects will then acquire that tag, also.
-
-* **tagged** (*rvalue*): A boolean function that tells you whether the current container is tagged with the specified tags. The tags are ANDed, so thta all of the specified tags must be included for the function to return true.
-
-* **template** (*rvalue*): Evaluate a template and return its value. See [the templating docs](/trac/puppet/wiki/PuppetTemplating) for more information. Note that if multiple templates are specified, their output is all concatenated and returned as the output of the function.
-
-* **warning** (*statement*): Log a message on the server at level warning.
-
-
-
-----------------
-
-
-*This page autogenerated on Fri Jan 26 16:40:49 CST 2007*
diff --git a/documentation/reference/index.page b/documentation/reference/index.page
deleted file mode 100644
index 809b854d3..000000000
--- a/documentation/reference/index.page
+++ /dev/null
@@ -1,18 +0,0 @@
----
-inMenu: false
-directoryName: Reference
-title: Reference
-orderInfo: 4
-subtreeLevel: 6
----
-
-Reference
-=========
-* [Configuration Parameter Reference](configref.html)
-* [Function Reference](functions.html)
-* [Reports Reference](reports.html)
-* [Type Reference](typedocs.html)
-
-
-
-*$Id: index.page 1843 2006-11-09 20:47:30Z luke $*
diff --git a/documentation/reference/reports.header b/documentation/reference/reports.header
deleted file mode 100644
index f919a1aca..000000000
--- a/documentation/reference/reports.header
+++ /dev/null
@@ -1,19 +0,0 @@
----
-inMenu: true
-title: Reports Reference
-orderInfo: 40
----
-
-Puppet clients can report back to the server after each
-transaction. This transaction report is sent as a YAML dump and includes every
-log message that was generated during the transaction along with as many metrics
-as Puppet knows how to collect.
-
-Currently, clients default to not sending in reports; you can enable reporting
-by setting the ``report`` parameter to true.
-
-To use a report, set the ``reports`` parameter on the server; multiple
-reports must be comma-separated.
-
-Puppet provides multiple report handlers that will process client reports:
-
diff --git a/documentation/reference/reports.page b/documentation/reference/reports.page
deleted file mode 100644
index 0319589ae..000000000
--- a/documentation/reference/reports.page
+++ /dev/null
@@ -1,82 +0,0 @@
----
-inMenu: true
-title: Reports Reference
-orderInfo: 40
----
-
-Puppet clients can report back to the server after each
-transaction. This transaction report is sent as a YAML dump and includes every
-log message that was generated during the transaction along with as many metrics
-as Puppet knows how to collect.
-
-Currently, clients default to not sending in reports; you can enable reporting
-by setting the ``report`` parameter to true.
-
-To use a report, set the ``reports`` parameter on the server; multiple
-reports must be comma-separated.
-
-Puppet provides multiple report handlers that will process client reports:
-
-## log
-
-Send all received logs to the local log destinations.
-
-## rrdgraph
-
-Graph all available data about hosts using the RRD library. You
-must have the RRD binary library installed to use this report, which
-you can get from [Tobias Oetiker's site](http://people.ee.ethz.ch/~oetiker/webtools/rrdtool/pub/contrib/).
-
-This report will create, manage, and graph RRD database files for each
-of the metrics generated during transactions, and it will create a
-few simple html files to display the reporting host's graphs. At this
-point, it will not create a common index file to display links to
-all hosts.
-
-All RRD files and graphs get created in the ``rrddir`` directory. If
-you want to serve these publicly, you should be able to just alias that
-directory in a web server.
-
-## store
-
-Store the yaml report on disk. Each host sends its report as a YAML dump
-and this just stores the file on disk, in the ``reportdir`` directory.
-
-These files collect quickly -- one every half hour -- so it is a good idea
-to perform some maintenance on them if you use this report (it's the only
-default report).
-
-## tagmail
-
-This report sends specific log messages to specific email addresses
-based on the tags in the log messages. See the
-[tag documentation](/trac/puppet/wiki/UsingTags) for more information
-on tags.
-
-To use this report, you must create a ``tagmail.conf`` (in the location
-specified by ``tagmap``). This is a simple file that maps tags to
-email addresses: Any log messages in the report that match the specified
-tags will be sent to the specified email addresses.
-
-Tags must be comma-separated, and they can be negated so that messages
-only match when they do not have that tag. The tags are separated from
-the email addresses by a colon, and the email addresses should also
-be comma-separated.
-
-Lastly, there is an ``all`` tag that will always match all log messages.
-
-Here is an example tagmail.conf:
-
- all: me@domain.com
- webserver, !mailserver: httpadmins@domain.com
-
-This will send all messages to ``me@domain.com``, and all messages from
-webservers that are not also from mailservers to ``httpadmins@domain.com``.
-
-
-
-
-----------------
-
-
-*This page autogenerated on Fri Jan 26 16:40:48 CST 2007*
diff --git a/documentation/reference/typedocs.header b/documentation/reference/typedocs.header
deleted file mode 100644
index 69fabd07c..000000000
--- a/documentation/reference/typedocs.header
+++ /dev/null
@@ -1,7 +0,0 @@
----
-inMenu: true
-title: Type Reference
-orderInfo: 40
----
-# Type Reference
-
diff --git a/documentation/reference/typedocs.page b/documentation/reference/typedocs.page
deleted file mode 100644
index 3fff7ff70..000000000
--- a/documentation/reference/typedocs.page
+++ /dev/null
@@ -1,1775 +0,0 @@
----
-inMenu: true
-title: Type Reference
-orderInfo: 40
----
-# Type Reference
-
-## Table of Contents
-1. <a href='#meta-parameters'>Meta-Parameters</a>
-1. <a href='#cron'>Cron</a>
-1. <a href='#exec'>Exec</a>
-1. <a href='#file'>File</a>
-1. <a href='#filebucket'>Filebucket</a>
-1. <a href='#group'>Group</a>
-1. <a href='#host'>Host</a>
-1. <a href='#mount'>Mount</a>
-1. <a href='#notify'>Notify</a>
-1. <a href='#package'>Package</a>
-1. <a href='#resources'>Resources</a>
-1. <a href='#schedule'>Schedule</a>
-1. <a href='#service'>Service</a>
-1. <a href='#sshkey'>Sshkey</a>
-1. <a href='#tidy'>Tidy</a>
-1. <a href='#user'>User</a>
-1. <a href='#yumrepo'>Yumrepo</a>
-1. <a href='#zone'>Zone</a>
-
-<h2><a name="meta-parameters">Meta-Parameters</a></h2>
-
-Metaparameters are parameters that work with any element; they are part of the
-Puppet framework itself rather than being part of the implementation of any
-given instance. Thus, any defined metaparameter can be used with any instance
-in your manifest, including defined components.
-
-
-#### alias
-Creates an alias for the object. Puppet uses this internally when you
-provide a symbolic name:
-
- file { sshdconfig:
- path => $operatingsystem ? {
- solaris => "/usr/local/etc/ssh/sshd_config",
- default => "/etc/ssh/sshd_config"
- },
- source => "..."
- }
-
- service { sshd:
- subscribe => file[sshdconfig]
- }
-
-When you use this feature, the parser sets ``sshdconfig`` as the name,
-and the library sets that as an alias for the file so the dependency
-lookup for ``sshd`` works. You can use this parameter yourself,
-but note that only the library can use these aliases; for instance,
-the following code will not work:
-
- file { "/etc/ssh/sshd_config":
- owner => root,
- group => root,
- alias => sshdconfig
- }
-
- file { sshdconfig:
- mode => 644
- }
-
-There's no way here for the Puppet parser to know that these two stanzas
-should be affecting the same file.
-
-See the [language tutorial][] for more information.
-
-[language tutorial]: languagetutorial.html
-
-
-#### before
-This parameter is the opposite of **require** -- it guarantees
-that the specified object is applied later than the specifying
-object:
-
- file { "/var/nagios/configuration":
- source => "...",
- recurse => true,
- before => exec["nagios-rebuid"]
- }
-
- exec { "nagios-rebuild":
- command => "/usr/bin/make",
- cwd => "/var/nagios/configuration"
- }
-
-This will make sure all of the files are up to date before the
-make command is run.
-
-#### check
-States which should have their values retrieved
-but which should not actually be modified. This is currently used
-internally, but will eventually be used for querying, so that you
-could specify that you wanted to check the install state of all
-packages, and then query the Puppet client daemon to get reports
-on all packages.
-
-#### loglevel
-Sets the level that information will be logged.
-The log levels have the biggest impact when logs are sent to
-syslog (which is currently the default). Valid values are ``debug``, ``info``, ``notice``, ``warning``, ``err``, ``alert``, ``emerg``, ``crit``, ``verbose``.
-
-#### noop
-Boolean flag indicating whether work should actually
-be done. Valid values are ``true``, ``false``.
-
-#### notify
-This parameter is the opposite of **subscribe** -- it sends events
-to the specified object:
-
- file { "/etc/sshd_config":
- source => "....",
- notify => service[sshd]
- }
-
- service { sshd:
- ensure => running
- }
-
-This will restart the sshd service if the sshd config file changes.
-
-#### require
-One or more objects that this object depends on.
-This is used purely for guaranteeing that changes to required objects
-happen before the dependent object. For instance:
-
- # Create the destination directory before you copy things down
- file { "/usr/local/scripts":
- ensure => directory
- }
-
- file { "/usr/local/scripts/myscript":
- source => "puppet://server/module/myscript",
- mode => 755,
- require => file["/usr/local/scripts"]
- }
-
-Note that Puppet will autorequire everything that it can, and
-there are hooks in place so that it's easy for elements to add new
-ways to autorequire objects, so if you think Puppet could be
-smarter here, let us know.
-
-In fact, the above code was redundant -- Puppet will autorequire
-any parent directories that are being managed; it will
-automatically realize that the parent directory should be created
-before the script is pulled down.
-
-Currently, exec elements will autorequire their CWD (if it is
-specified) plus any fully qualified paths that appear in the
-command. For instance, if you had an ``exec`` command that ran
-the ``myscript`` mentioned above, the above code that pulls the
-file down would be automatically listed as a requirement to the
-``exec`` code, so that you would always be running againts the
-most recent version.
-
-#### schedule
-On what schedule the object should be managed. You must create a
-schedule object, and then reference the name of that object to use
-that for your schedule:
-
- schedule { daily:
- period => daily,
- range => "2-4"
- }
-
- exec { "/usr/bin/apt-get update":
- schedule => daily
- }
-
-The creation of the schedule object does not need to appear in the
-configuration before objects that use it.
-
-#### subscribe
-One or more objects that this object depends on. Changes in the
-subscribed to objects result in the dependent objects being
-refreshed (e.g., a service will get restarted). For instance:
-
- class nagios {
- file { "/etc/nagios/nagios.conf":
- source => "puppet://server/module/nagios.conf",
- alias => nagconf # just to make things easier for me
- }
- service { nagios:
- running => true,
- subscribe => file[nagconf]
- }
- }
-
-#### tag
-Add the specified tags to the associated element. While all elements
-are automatically tagged with as much information as possible
-(e.g., each class and component containing the element), it can
-be useful to add your own tags to a given element.
-
-Tags are currently useful for things like applying a subset of a
-host's configuration:
-
- puppetd --test --tag mytag
-
-This way, when you're testing a configuration you can run just the
-portion you're testing.
-
-
-## Types
-
-- *namevar* is the parameter used to uniquely identify a type instance.
- This is the parameter that gets assigned when a string is provided before
- the colon in a type declaration. In general, only developers will need to
- worry about which parameter is the ``namevar``.
-
- In the following code:
-
- file { "/etc/passwd":
- owner => root,
- group => root,
- mode => 644
- }
-
- "/etc/passwd" is considered the name of the file object (used for things like
- dependency handling), and because ``path`` is the namevar for ``file``, that
- string is assigned to the ``path`` parameter.
-
-- *parameters* determine the specific configuration of the instance. They either
- directly modify the system (internally, these are called states) or they affect
- how the instance behaves (e.g., adding a search path for ``exec`` instances
- or determining recursion on ``file`` instances).
-
-When required binaries are specified for providers, fully qualifed paths
-indicate that the binary must exist at that specific path and unqualified
-binaries indicate that Puppet will search for the binary using the shell
-path.
-
-
-
-
-----------------
-
-
-<h2><a name='cron'>cron</a></h2>
-Installs and manages cron jobs. All fields except the command
-and the user are optional, although specifying no periodic
-fields would result in the command being executed every
-minute. While the name of the cron job is not part of the actual
-job, it is used by Puppet to store and retrieve it.
-
-If you specify a cron job that matches an existing job in every way
-except name, then the jobs will be considered equivalent and the
-new name will be permanently associated with that job. Once this
-association is made and synced to disk, you can then manage the job
-normally (e.g., change the schedule of the job).
-
-Example:
-
- cron { logrotate:
- command => "/usr/sbin/logrotate",
- user => root,
- hour => 2,
- minute => 0
- }
-
-
-
-### Cron Parameters
-#### command
-The command to execute in the cron job. The environment
-provided to the command varies by local system rules, and it is
-best to always provide a fully qualified command. The user's
-profile is not sourced when the command is run, so if the
-user's environment is desired it should be sourced manually.
-
-All cron parameters support ``absent`` as a value; this will
-remove any existing values for that field.
-
-#### ensure
-The basic state that the object should be in. Valid values are ``absent``, ``present``.
-
-#### environment
-Any environment settings associated with this cron job. They
-will be stored between the header and the job in the crontab. There
-can be no guarantees that other, earlier settings will not also
-affect a given cron job.
-
-Also, Puppet cannot automatically determine whether an existing,
-unmanaged environment setting is associated with a given cron
-job. If you already have cron jobs with environment settings,
-then Puppet will keep those settings in the same place in the file,
-but will not associate them with a specific job.
-
-Settings should be specified exactly as they should appear in
-the crontab, e.g., 'PATH=/bin:/usr/bin:/usr/sbin'. Multiple
-settings should be specified as an array.
-
-#### hour
-The hour at which to run the cron job. Optional;
-if specified, must be between 0 and 23, inclusive.
-
-#### minute
-The minute at which to run the cron job.
-Optional; if specified, must be between 0 and 59, inclusive.
-
-#### month
-The month of the year. Optional; if specified
-must be between 1 and 12 or the month name (e.g., December).
-
-#### monthday
-The day of the month on which to run the
-command. Optional; if specified, must be between 1 and 31.
-
-#### name (*namevar*)
-The symbolic name of the cron job. This name
-is used for human reference only and is generated automatically
-for cron jobs found on the system. This generally won't
-matter, as Puppet will do its best to match existing cron jobs
-against specified jobs (and Puppet adds a comment to cron jobs it
-adds), but it is at least possible that converting from
-unmanaged jobs to managed jobs might require manual
-intervention.
-
-The names can only have alphanumeric characters plus the '-'
-character.
-
-#### special
-Special schedules only supported on FreeBSD.
-
-#### user
-The user to run the command as. This user must
-be allowed to run cron jobs, which is not currently checked by
-Puppet.
-
-The user defaults to whomever Puppet is running as.
-
-#### weekday
-The weekday on which to run the command.
-Optional; if specified, must be between 0 and 6, inclusive, with
-0 being Sunday, or must be the name of the day (e.g., Tuesday).
-
-
-
-
-----------------
-
-
-<h2><a name='exec'>exec</a></h2>
-Executes external commands. It is critical that all commands
-executed using this mechanism can be run multiple times without
-harm, i.e., they are *idempotent*. One useful way to create idempotent
-commands is to use the *creates* parameter.
-
-It is worth noting that ``exec`` is special, in that it is not
-currently considered an error to have multiple ``exec`` instances
-with the same name. This was done purely because it had to be this
-way in order to get certain functionality, but it complicates things.
-In particular, you will not be able to use ``exec`` instances that
-share their commands with other instances as a dependency, since
-Puppet has no way of knowing which instance you mean.
-
-For example:
-
- # defined in the production class
- exec { "make":
- cwd => "/prod/build/dir",
- path => "/usr/bin:/usr/sbin:/bin"
- }
-
- . etc. .
-
- # defined in the test class
- exec { "make":
- cwd => "/test/build/dir",
- path => "/usr/bin:/usr/sbin:/bin"
- }
-
-Any other type would throw an error, complaining that you had
-the same instance being managed in multiple places, but these are
-obviously different images, so ``exec`` had to be treated specially.
-
-It is recommended to avoid duplicate names whenever possible.
-
-There is a strong tendency to use ``exec`` to do whatever work Puppet
-can't already do; while this is obviously acceptable (and unavoidable)
-in the short term, it is highly recommended to migrate work from ``exec``
-to real Puppet element types as quickly as possible. If you find that
-you are doing a lot of work with ``exec``, please at least notify
-us at Reductive Labs what you are doing, and hopefully we can work with
-you to get a native element type for the work you are doing. In general,
-it is a Puppet bug if you need ``exec`` to do your work.
-
-
-### Exec Parameters
-#### command (*namevar*)
-The actual command to execute. Must either be fully qualified
-or a search path for the command must be provided. If the command
-succeeds, any output produced will be logged at the instance's
-normal log level (usually ``notice``), but if the command fails
-(meaning its return code does not match the specified code) then
-any output is logged at the ``err`` log level.
-
-#### creates
-A file that this command creates. If this
-parameter is provided, then the command will only be run
-if the specified file does not exist.
-
- exec { "tar xf /my/tar/file.tar":
- cwd => "/var/tmp",
- creates => "/var/tmp/myfile",
- path => ["/usr/bin", "/usr/sbin"]
- }
-
-
-#### cwd
-The directory from which to run the command. If
-this directory does not exist, the command will fail.
-
-#### env
-Any additional environment variables you want to set for a
-command. Note that if you use this to set PATH, it will override
-the ``path`` attribute. Multiple environment variables should be
-specified as an array.
-
-#### group
-The group to run the command as. This seems to work quite
-haphazardly on different platforms -- it is a platform issue
-not a Ruby or Puppet one, since the same variety exists when
-running commnands as different users in the shell.
-
-#### logoutput
-Whether to log output. Defaults to logging output at the
-loglevel for the ``exec`` element. Values are **true**, *false*,
-and any legal log level. Valid values are ``true``, ``false``, ``debug``, ``info``, ``notice``, ``warning``, ``err``, ``alert``, ``emerg``, ``crit``.
-
-#### onlyif
-If this parameter is set, then this ``exec`` will only run if
-the command returns 0. For example:
-
- exec { "logrotate":
- path => "/usr/bin:/usr/sbin:/bin",
- onlyif => "test `du /var/log/messages | cut -f1` -gt 100000"
- }
-
-This would run ``logrotate`` only if that test returned true.
-
-Note that this command follows the same rules as the main command,
-which is to say that it must be fully qualified if the path is not set.
-
-#### path
-The search path used for command execution.
-Commands must be fully qualified if no path is specified. Paths
-can be specified as an array or as a colon-separated list.
-
-#### refreshonly
-The command should only be run as a
-refresh mechanism for when a dependent object is changed. It only
-makes sense to use this option when this command depends on some
-other object; it is useful for triggering an action:
-
- # Pull down the main aliases file
- file { "/etc/aliases":
- source => "puppet://server/module/aliases"
- }
-
- # Rebuild the database, but only when the file changes
- exec { newaliases:
- path => ["/usr/bin", "/usr/sbin"],
- subscribe => file["/etc/aliases"],
- refreshonly => true
- }
-
-Note that only ``subscribe`` can trigger actions, not ``require``,
-so it only makes sense to use ``refreshonly`` with ``subscribe``. Valid values are ``true``, ``false``.
-
-#### returns
-The expected return code. An error will be returned if the
-executed command returns something else. Defaults to 0.
-
-#### timeout
-The maximum time the command should take. If the command takes
-longer than the timeout, the command is considered to have failed
-and will be stopped. Use any negative number to disable the timeout.
-
-#### unless
-If this parameter is set, then this ``exec`` will run unless
-the command returns 0. For example:
-
- exec { "/bin/echo root >> /usr/lib/cron/cron.allow":
- path => "/usr/bin:/usr/sbin:/bin",
- unless => "grep root /usr/lib/cron/cron.allow 2>/dev/null"
- }
-
-This would add ``root`` to the cron.allow file (on Solaris) unless
-``grep`` determines it's already there.
-
-Note that this command follows the same rules as the main command,
-which is to say that it must be fully qualified if the path is not set.
-
-#### user
-The user to run the command as. Note that if you
-use this then any error output is not currently captured. This
-is because of a bug within Ruby.
-
-
-
-
-----------------
-
-
-<h2><a name='file'>file</a></h2>
-Manages local files, including setting ownership and
-permissions, creation of both files and directories, and
-retrieving entire files from remote servers. As Puppet matures, it
-expected that the ``file`` element will be used less and less to
-manage content, and instead native elements will be used to do so.
-
-If you find that you are often copying files in from a central
-location, rather than using native elements, please contact
-Reductive Labs and we can hopefully work with you to develop a
-native element to support what you are doing.
-
-
-### File Parameters
-#### backup
-Whether files should be backed up before
-being replaced. The preferred method of backing files up is via
-a ``filebucket``, which stores files by their MD5 sums and allows
-easy retrieval without littering directories with backups. You
-can specify a local filebucket or a network-accessible
-server-based filebucket. Alternatively, if you specify any
-value that begins with a ``.`` (e.g., ``.puppet-bak``), then
-Puppet will use copy the file in the same directory with that
-value as the extension of the backup.
-
-Puppet automatically creates a local filebucket named ``puppet`` and
-defaults to backing up there. To use a server-based filebucket,
-you must specify one in your configuration:
-
- filebucket { main:
- server => puppet
- }
-
-The ``puppetmasterd`` daemon creates a filebucket by default,
-so you can usually back up to your main server with this
-configuration. Once you've described the bucket in your
-configuration, you can use it in any file:
-
- file { "/my/file":
- source => "/path/in/nfs/or/something",
- backup => main
- }
-
-This will back the file up to the central server.
-
-At this point, the benefits of using a filebucket are that you do not
-have backup files lying around on each of your machines, a given
-version of a file is only backed up once, and you can restore
-any given file manually, no matter how old. Eventually,
-transactional support will be able to automatically restore
-filebucketed files.
-
-#### checksum
-How to check whether a file has changed. This state is used internally
-for file copying, but it can also be used to monitor files somewhat
-like Tripwire without managing the file contents in any way. You can
-specify that a file's checksum should be monitored and then subscribe to
-the file from another object and receive events to signify
-checksum changes, for instance. Valid values are ``time``, ``md5lite``, ``nosum``, ``timestamp``, ``md5``, ``mtime``. Values can also match ``(?-mix:^\{md5|md5lite|timestamp|mtime|time\})``.
-
-#### content
-Specify the contents of a file as a string. Newlines, tabs, and
-spaces can be specified using the escaped syntax (e.g., \n for a
-newline). The primary purpose of this parameter is to provide a
-kind of limited templating:
-
- define resolve(nameserver1, nameserver2, domain, search) {
- $str = "search $search
- domain $domain
- nameserver $nameserver1
- nameserver $nameserver2
- "
-
- file { "/etc/resolv.conf":
- content => $str
- }
- }
-
- This attribute is especially useful when used with
- [templating](/trac/puppet/wiki/PuppetTemplating).
-
-#### ensure
-Whether to create files that don't currently exist.
-Possible values are *absent*, *present* (equivalent to ``exists`` in
-most file tests -- will match any form of file existence, and if the
-file is missing will create an empty file), *file*, and
-*directory*. Specifying ``absent`` will delete the file, although
-currently this will not recursively delete directories.
-
-Anything other than those values will be considered to be a symlink.
-For instance, the following text creates a link:
-
- # Useful on solaris
- file { "/etc/inetd.conf":
- ensure => "/etc/inet/inetd.conf"
- }
-
-You can make relative links:
-
- # Useful on solaris
- file { "/etc/inetd.conf":
- ensure => "inet/inetd.conf"
- }
-
-If you need to make a relative link to a file named the same
-as one of the valid values, you must prefix it with ``./`` or
-something similar.
-
-You can also make recursive symlinks, which will create a
-directory structure that maps to the target directory,
-with directories corresponding to each directory
-and links corresponding to each file. Valid values are ``link``, ``present``, ``absent`` (also called ``false``), ``directory``, ``file``. Values can also match ``(?-mix:.)``.
-
-#### force
-Force the file operation. Currently only used when replacing
-directories with links. Valid values are ``true``, ``false``.
-
-#### group
-Which group should own the file. Argument can be either group
-name or group ID.
-
-#### ignore
-A parameter which omits action on files matching
-specified patterns during recursion. Uses Ruby's builtin globbing
-engine, so shell metacharacters are fully supported, e.g. ``[a-z]*``.
-Matches that would descend into the directory structure are ignored,
-e.g., ``*/*``.
-
-#### linkmaker
-An internal parameter used by the *symlink*
-type to do recursive link creation.
-
-#### links
-How to handle links during file actions. During file copying,
-``follow`` will copy the target file instead of the link, ``manage``
-will copy the link itself, and ``ignore`` will just pass it by.
-When not copying, ``manage`` and ``ignore`` behave equivalently
-(because you cannot really ignore links entirely during local
-recursion), and ``follow`` will manage the file to which the
-link points. Valid values are ``follow``, ``manage``, ``ignore``.
-
-#### mode
-Mode the file should be. Currently relatively limited:
-you must specify the exact mode the file should be.
-
-#### owner
-To whom the file should belong. Argument can be user name or
-user ID.
-
-#### path (*namevar*)
-The path to the file to manage. Must be fully qualified.
-
-#### purge
-Whether unmanaged files should be purged. If you have a filebucket
-configured the purged files will be uploaded, but if you do not,
-this will destroy data. Only use this option for generated
-files unless you really know what you are doing. This option only
-makes sense when recursively managing directories. Valid values are ``true``, ``false``.
-
-#### recurse
-Whether and how deeply to do recursive
-management. Valid values are ``true``, ``false``, ``inf``. Values can also match ``(?-mix:^[0-9]+$)``.
-
-#### replace
-Whether or not to replace a file that is
-sourced but exists. This is useful for using file sources
-purely for initialization. Valid values are ``true`` (also called ``yes``), ``false`` (also called ``no``).
-
-#### source
-Copy a file over the current file. Uses ``checksum`` to
-determine when a file should be copied. Valid values are either
-fully qualified paths to files, or URIs. Currently supported URI
-types are *puppet* and *file*.
-
-This is one of the primary mechanisms for getting content into
-applications that Puppet does not directly support and is very
-useful for those configuration files that don't change much across
-sytems. For instance:
-
- class sendmail {
- file { "/etc/mail/sendmail.cf":
- source => "puppet://server/module/sendmail.cf"
- }
- }
-
-See the [fileserver docs][] for information on how to configure
-and use file services within Puppet.
-
-If you specify multiple file sources for a file, then the first
-source that exists will be used. This allows you to specify
-what amount to search paths for files:
-
- file { "/path/to/my/file":
- source => [
- "/nfs/files/file.$host",
- "/nfs/files/file.$operatingsystem",
- "/nfs/files/file"
- ]
- }
-
-This will use the first found file as the source.
-
-You cannot currently copy links using this mechanism; set ``links``
-to ``follow`` if any remote sources are links.
-
-[fileserver docs]: ../installing/fsconfigref.html
-
-
-#### sourceselect
-Whether to copy all valid sources, or just the first one. Valid values are ``first``, ``all``.
-
-#### target
-The target for creating a link. Currently, symlinks are the
-only type supported. Valid values are ``notlink``. Values can also match ``(?-mix:.)``.
-
-#### type
-A read-only state to check the file type.
-
-
-
-
-----------------
-
-
-<h2><a name='filebucket'>filebucket</a></h2>
-A repository for backing up files. If no filebucket is
-defined, then files will be backed up in their current directory,
-but the filebucket can be either a host- or site-global repository
-for backing up. It stores files and returns the MD5 sum, which
-can later be used to retrieve the file if restoration becomes
-necessary. A filebucket does not do any work itself; instead,
-it can be specified as the value of *backup* in a **file** object.
-
-Currently, filebuckets are only useful for manual retrieval of
-accidentally removed files (e.g., you look in the log for the md5
-sum and retrieve the file with that sum from the filebucket), but
-when transactions are fully supported filebuckets will be used to
-undo transactions.
-
-You will normally want to define a single filebucket for your
-whole network and then use that as the default backup location:
-
- # Define the bucket
- filebucket { main: server => puppet }
-
- # Specify it as the default target
- File { backup => main }
-
-Puppetmaster servers create a filebucket by default, so this will
-work in a default configuration.
-
-
-
-### Filebucket Parameters
-#### name (*namevar*)
-The name of the filebucket.
-
-#### path
-The path to the local filebucket. If this is
-not specified, then the bucket is remote and *server* must be
-specified.
-
-#### port
-The port on which the remote server is listening.
-Defaults to the normal Puppet port, 8140.
-
-#### server
-The server providing the filebucket. If this is
-not specified, then the bucket is local and *path* must be
-specified.
-
-
-
-
-----------------
-
-
-<h2><a name='group'>group</a></h2>
-Manage groups. This type can only create groups. Group
-membership must be managed on individual users. This element type
-uses the prescribed native tools for creating groups and generally
-uses POSIX APIs for retrieving information about them. It does
-not directly modify /etc/group or anything.
-
-For most platforms, the tools used are ``groupadd`` and its ilk;
-for Mac OS X, NetInfo is used. This is currently unconfigurable,
-but if you desperately need it to be so, please contact us.
-
-
-### Group Parameters
-#### allowdupe
-Whether to allow duplicate GIDs. This option does not work on
-FreeBSD (contract to the ``pw`` man page). Valid values are ``true``, ``false``.
-
-#### ensure
-The basic state that the object should be in. Valid values are ``absent``, ``present``.
-
-#### gid
-The group ID. Must be specified numerically. If not
-specified, a number will be picked, which can result in ID
-differences across systems and thus is not recommended. The
-GID is picked according to local system standards.
-
-#### name (*namevar*)
-The group name. While naming limitations vary by
-system, it is advisable to keep the name to the degenerate
-limitations, which is a maximum of 8 characters beginning with
-a letter.
-
-#### provider
-The specific backend for provider to use. You will
-seldom need to specify this -- Puppet will usually discover the
-appropriate provider for your platform. Available providers are:
-
-* **groupadd**: Group management via ``groupadd`` and its ilk. The default
- for most platforms Required binaries: ``groupadd``, ``groupmod``, ``groupdel``.
-* **netinfo**: Group management using NetInfo. Default for ``operatingsystem`` == ``darwin``. Required binaries: ``nireport``, ``niutil``.
-* **pw**: Group management via ``pw``. Only works on FreeBSD. Default for ``operatingsystem`` == ``freebsd``. Required binaries: ``/usr/sbin/pw``.
-
-
-
-
-----------------
-
-
-<h2><a name='host'>host</a></h2>
-Installs and manages host entries. For most systems, these
-entries will just be in /etc/hosts, but some systems (notably OS X)
-will have different solutions.
-
-
-### Host Parameters
-#### alias
-Any alias the host might have. Multiple values must be
-specified as an array. Note that this state has the same name
-as one of the metaparams; using this state to set aliases will
-make those aliases available in your Puppet scripts and also on
-disk.
-
-#### ensure
-The basic state that the object should be in. Valid values are ``absent``, ``present``.
-
-#### ip
-The host's IP address, IPv4 or IPv6.
-
-#### name (*namevar*)
-The host name.
-
-#### provider
-The specific backend for provider to use. You will
-seldom need to specify this -- Puppet will usually discover the
-appropriate provider for your platform. Available providers are:
-
-* **netinfo**: Host management in NetInfo. This provider is highly experimental and is known
- not to work currently. Default for ``operatingsystem`` == ``darwin``. Required binaries: ``nireport``, ``mount``, ``niutil``, ``df``, ``umount``.
-* **parsed**:
-
-#### target
-The file in which to store service information. Only used by
-those providers that write to disk (i.e., not NetInfo).
-
-
-
-
-----------------
-
-
-<h2><a name='mount'>mount</a></h2>
-Manages mounted mounts, including putting mount
-information into the mount table. The actual behavior depends
-on the value of the 'ensure' parameter.
-
-
-### Mount Parameters
-#### atboot
-Whether to mount the mount at boot. Not all platforms
-support this.
-
-#### blockdevice
-The the device to fsck. This is state is only valid
-on Solaris, and in most cases will default to the correct
-value.
-
-#### device
-The device providing the mount. This can be whatever
-device is supporting by the mount, including network
-devices or devices specified by UUID rather than device
-path, depending on the operating system.
-
-#### dump
-Whether to dump the mount. Not all platforms
-support this.
-
-#### ensure
-Control what to do with this mount. If the value is
-``present``, the mount is entered into the mount table,
-but not mounted, if it is ``absent``, the entry is removed
-from the mount table and the filesystem is unmounted if
-currently mounted, if it is ``mounted``, the filesystem
-is entered into the mount table and mounted. Valid values are ``absent``, ``present`` (also called ``unmounted``), ``mounted``.
-
-#### fstype
-The mount type. Valid values depend on the
-operating system.
-
-#### name (*namevar*)
-The mount path for the mount.
-
-#### options
-Mount options for the mounts, as they would
-appear in the fstab.
-
-#### pass
-The pass in which the mount is checked.
-
-#### path
-The deprecated name for the mount point. Please use ``name`` now.
-
-#### provider
-The specific backend for provider to use. You will
-seldom need to specify this -- Puppet will usually discover the
-appropriate provider for your platform. Available providers are:
-
-* **netinfo**: Mount management in NetInfo. This provider is highly experimental and is known
- not to work currently. Default for ``operatingsystem`` == ``darwin``. Required binaries: ``nireport``, ``mount``, ``niutil``, ``df``, ``umount``.
-* **parsed**: Required binaries: ``mount``, ``df``, ``umount``.
-
-#### target
-The file in which to store the mount table. Only used by
-those providers that write to disk (i.e., not NetInfo).
-
-
-
-
-----------------
-
-
-<h2><a name='notify'>notify</a></h2>
-Sends an arbitrary message to the puppetd run-time log.
-
-
-### Notify Parameters
-#### message
-The message to be sent to the log.
-
-#### name (*namevar*)
-An arbitrary tag for your own reference; the name of the message.
-
-#### withpath
-Whether to not to show the full object path. Sends the
-message at the current loglevel. Valid values are ``true``, ``false``.
-
-
-
-
-----------------
-
-
-<h2><a name='package'>package</a></h2>
-Manage packages. There is a basic dichotomy in package
-support right now: Some package types (e.g., yum and apt) can
-retrieve their own package files, while others (e.g., rpm and
-sun) cannot. For those package formats that cannot retrieve
-their own files, you can use the ``source`` parameter to point to
-the correct file.
-
-Puppet will automatically guess the packaging format that you are
-using based on the platform you are on, but you can override it
-using the ``type`` parameter; obviously, if you specify that you
-want to use ``rpm`` then the ``rpm`` tools must be available.
-
-
-### Package Parameters
-#### adminfile
-A file containing package defaults for installing packages.
-This is currently only used on Solaris. The value will be
-validated according to system rules, which in the case of
-Solaris means that it should either be a fully qualified path
-or it should be in /var/sadm/install/admin.
-
-#### allowcdrom
-Tells apt to allow cdrom sources in the sources.list file.
-Normally apt will bail if you try this. Valid values are ``true``, ``false``.
-
-#### category
-A read-only parameter set by the package.
-
-#### configfiles
-Whether configfiles should be kept or replaced. Most packages
-types do not support this parameter. Valid values are ``keep``, ``replace``.
-
-#### description
-A read-only parameter set by the package.
-
-#### ensure
-What state the package should be in.
-*latest* only makes sense for those packaging formats that can
-retrieve new packages on their own and will throw an error on
-those that cannot. For those packaging systems that allow you
-to specify package versions, specify them here. Valid values are ``absent``, ``present`` (also called ``installed``), ``latest``. Values can also match ``(?-mix:.)``.
-
-#### instance
-A read-only parameter set by the package.
-
-#### name (*namevar*)
-The package name. This is the name that the packaging
-system uses internally, which is sometimes (especially on Solaris)
-a name that is basically useless to humans. If you want to
-abstract package installation, then you can use aliases to provide
-a common name to packages:
-
- # In the 'openssl' class
- $ssl = $operationgsystem ? {
- solaris => SMCossl,
- default => openssl
- }
-
- # It is not an error to set an alias to the same value as the
- # object name.
- package { $ssl:
- ensure => installed,
- alias => openssl
- }
-
- . etc. .
-
- $ssh = $operationgsystem ? {
- solaris => SMCossh,
- default => openssh
- }
-
- # Use the alias to specify a dependency, rather than
- # having another selector to figure it out again.
- package { $ssh:
- ensure => installed,
- alias => openssh,
- require => package[openssl]
- }
-
-
-#### platform
-A read-only parameter set by the package.
-
-#### provider
-The specific backend for provider to use. You will
-seldom need to specify this -- Puppet will usually discover the
-appropriate provider for your platform. Available providers are:
-
-* **apple**: Package management based on OS X's builtin packaging system. This is
- essentially the simplest and least functional package system in existence --
- it only supports installation; no deletion or upgrades. Default for ``operatingsystem`` == ``darwin``. Required binaries: ``/usr/sbin/installer``.
-* **apt**: Package management via ``apt-get``. Default for ``operatingsystem`` == ``debian``. Required binaries: ``/usr/bin/apt-get``, ``/usr/bin/apt-cache``, ``/usr/bin/debconf-set-selections``.
-* **aptitude**: Package management via ``aptitude``. Required binaries: ``/usr/bin/apt-cache``, ``/usr/bin/aptitude``.
-* **blastwave**: Package management using Blastwave.org's ``pkg-get`` command on Solaris. Required binaries: ``pkg-get``.
-* **darwinport**: Package management using DarwinPorts on OS X. Required binaries: ``/opt/local/bin/port``.
-* **dpkg**: Package management via ``dpkg``. Because this only uses ``dpkg``
- and not ``apt``, you must specify the source of any packages you want
- to manage. Required binaries: ``/usr/bin/dpkg``, ``/usr/bin/dpkg-query``.
-* **freebsd**: The specific form of package management on FreeBSD. This is an
- extremely quirky packaging system, in that it freely mixes between
- ports and packages. Apparently all of the tools are written in Ruby,
- so there are plans to rewrite this support to directly use those
- libraries. Required binaries: ``/usr/sbin/pkg_info``, ``/usr/sbin/pkg_add``, ``/usr/sbin/pkg_delete``.
-* **gem**: Ruby Gem support. By default uses remote gems, but you can specify
- the path to a local gem via ``source``. Required binaries: ``gem``.
-* **openbsd**: OpenBSD's form of ``pkg_add`` support. Default for ``operatingsystem`` == ``openbsd``. Required binaries: ``pkg_info``, ``pkg_add``, ``pkg_delete``.
-* **pkgdmg**: Package management based on Apple's Installer.app and DiskUtility.app Required binaries: ``/usr/sbin/installer``, ``/usr/bin/hdiutil``, ``/usr/bin/curl``.
-* **portage**: Provides packaging support for Gentoo's portage system. Default for ``operatingsystem`` == ``gentoo``. Required binaries: ``/usr/bin/emerge``, ``/usr/bin/eix``.
-* **ports**: Support for FreeBSD's ports. Again, this still mixes packages
- and ports. Default for ``operatingsystem`` == ``freebsd``. Required binaries: ``/usr/local/sbin/portupgrade``, ``/usr/local/sbin/portversion``, ``/usr/local/sbin/pkg_deinstall``, ``/usr/sbin/pkg_info``.
-* **rpm**: RPM packaging support; should work anywhere with a working ``rpm``
- binary. Required binaries: ``rpm``.
-* **sun**: Sun's packaging system. Requires that you specify the source for
- the packages you're managing. Default for ``operatingsystem`` == ``solaris``. Required binaries: ``/usr/bin/pkginfo``, ``/usr/sbin/pkgrm``, ``/usr/sbin/pkgadd``.
-* **sunfreeware**: Package management using sunfreeware.com's ``pkg-get`` command on Solaris.
- At this point, support is exactly the same as ``blastwave`` support and
- has not actually been tested. Required binaries: ``pkg-get``.
-* **up2date**: Support for Red Hat's proprietary ``up2date`` package update
- mechanism. Default for ``operatingsystem`` == ``redhat``. Required binaries: ``/usr/sbin/up2date-nox``.
-* **yum**: Support via ``yum``. Default for ``operatingsystem`` == ``fedoracentos``. Required binaries: ``yum``, ``rpm``.
-
-#### responsefile
-A file containing any necessary answers to questions asked by
-the package. This is currently only used on Solaris. The
-value will be validated according to system rules, but it should
-generally be a fully qualified path.
-
-#### root
-A read-only parameter set by the package.
-
-#### source
-Where to find the actual package. This must be a local file
-(or on a network file system) or a URL that your specific
-packaging type understands; Puppet will not retrieve files for you.
-
-#### status
-A read-only parameter set by the package.
-
-#### type
-Deprecated form of ``provider``.
-
-#### vendor
-A read-only parameter set by the package.
-
-
-
-
-----------------
-
-
-<h2><a name='resources'>resources</a></h2>
-This is a metatype that can manage other resource types. Any
-metaparams specified here will be passed on to any generated resources,
-so you can purge umanaged resources but set ``noop`` to true so the
-purging is only logged and does not actually happen.
-
-
-### Resources Parameters
-#### name (*namevar*)
-The name of the type to be managed.
-
-#### purge
-Purge unmanaged resources. This will delete any resource
-that is not specified in your configuration
-and is not required by any specified resources. Valid values are ``true``, ``false``.
-
-#### unless_system_user
-This keeps system users from being purged. By default, it
-does not purge users whose UIDs are less than or equal to 500, but you can specify
-a different UID as the inclusive limit. Valid values are ``true``, ``false``. Values can also match ``(?-mix:^\d+$)``.
-
-
-
-
-----------------
-
-
-<h2><a name='schedule'>schedule</a></h2>
-Defined schedules for Puppet. The important thing to understand
-about how schedules are currently implemented in Puppet is that they
-can only be used to stop an element from being applied, they never
-guarantee that it is applied.
-
-Every time Puppet applies its configuration, it will collect the
-list of elements whose schedule does not eliminate them from
-running right then, but there is currently no system in place to
-guarantee that a given element runs at a given time. If you
-specify a very restrictive schedule and Puppet happens to run at a
-time within that schedule, then the elements will get applied;
-otherwise, that work may never get done.
-
-Thus, it behooves you to use wider scheduling (e.g., over a couple of
-hours) combined with periods and repetitions. For instance, if you
-wanted to restrict certain elements to only running once, between
-the hours of two and 4 AM, then you would use this schedule:
-
- schedule { maint:
- range => "2 - 4",
- period => daily,
- repeat => 1
- }
-
-With this schedule, the first time that Puppet runs between 2 and 4 AM,
-all elements with this schedule will get applied, but they won't
-get applied again between 2 and 4 because they will have already
-run once that day, and they won't get applied outside that schedule
-because they will be outside the scheduled range.
-
-Puppet automatically creates a schedule for each valid period with the
-same name as that period (e.g., hourly and daily). Additionally,
-a schedule named *puppet* is created and used as the default,
-with the following attributes:
-
- schedule { puppet:
- period => hourly,
- repeat => 2
- }
-
-This will cause elements to be applied every 30 minutes by default.
-
-
-
-### Schedule Parameters
-#### name (*namevar*)
-The name of the schedule. This name is used to retrieve the
-schedule when assigning it to an object:
-
- schedule { daily:
- period => daily,
- range => [2, 4]
- }
-
- exec { "/usr/bin/apt-get update":
- schedule => daily
- }
-
-
-#### period
-The period of repetition for an element. Choose from among
-a fixed list of *hourly*, *daily*, *weekly*, and *monthly*.
-The default is for an element to get applied every time that
-Puppet runs, whatever that period is.
-
-Note that the period defines how often a given element will get
-applied but not when; if you would like to restrict the hours
-that a given element can be applied (e.g., only at night during
-a maintenance window) then use the ``range`` attribute.
-
-If the provided periods are not sufficient, you can provide a
-value to the *repeat* attribute, which will cause Puppet to
-schedule the affected elements evenly in the period the
-specified number of times. Take this schedule:
-
- schedule { veryoften:
- period => hourly,
- repeat => 6
- }
-
-This can cause Puppet to apply that element up to every 10 minutes.
-
-At the moment, Puppet cannot guarantee that level of
-repetition; that is, it can run up to every 10 minutes, but
-internal factors might prevent it from actually running that
-often (e.g., long-running Puppet runs will squash conflictingly
-scheduled runs).
-
-See the ``periodmatch`` attribute for tuning whether to match
-times by their distance apart or by their specific value. Valid values are ``hourly``, ``daily``, ``weekly``, ``monthly``.
-
-#### periodmatch
-Whether periods should be matched by number (e.g., the two times
-are in the same hour) or by distance (e.g., the two times are
-60 minutes apart). *number*/**distance** Valid values are ``number``, ``distance``.
-
-#### range
-The earliest and latest that an element can be applied. This
-is always a range within a 24 hour period, and hours must be
-specified in numbers between 0 and 23, inclusive. Minutes and
-seconds can be provided, using the normal colon as a separator.
-For instance:
-
- schedule { maintenance:
- range => "1:30 - 4:30"
- }
-
-This is mostly useful for restricting certain elements to being
-applied in maintenance windows or during off-peak hours.
-
-#### repeat
-How often the application gets repeated in a given period.
-Defaults to 1. Must be an integer.
-
-
-
-
-----------------
-
-
-<h2><a name='service'>service</a></h2>
-Manage running services. Service support unfortunately varies
-widely by platform -- some platforms have very little if any
-concept of a running service, and some have a very codified and
-powerful concept. Puppet's service support will generally be able
-to make up for any inherent shortcomings (e.g., if there is no
-'status' command, then Puppet will look in the process table for a
-command matching the service name), but the more information you
-can provide the better behaviour you will get. Or, you can just
-use a platform that has very good service support.
-
-
-### Service Parameters
-#### binary
-The path to the daemon. This is only used for
-systems that do not support init scripts. This binary will be
-used to start the service if no ``start`` parameter is
-provided.
-
-#### enable
-Whether a service should be enabled to start at boot.
-This state behaves quite differently depending on the platform;
-wherever possible, it relies on local tools to enable or disable
-a given service. *true*/*false*/*runlevels* Valid values are ``true``, ``false``.
-
-#### ensure
-Whether a service should be running. **true**/*false* Valid values are ``running`` (also called ``true``), ``stopped`` (also called ``false``).
-
-#### hasrestart
-Specify that an init script has a ``restart`` option. Otherwise,
-the init script's ``stop`` and ``start`` methods are used. Valid values are ``true``, ``false``.
-
-#### hasstatus
-Declare the the service's init script has a
-functional status command. Based on testing, it was found
-that a large number of init scripts on different platforms do
-not support any kind of status command; thus, you must specify
-manually whether the service you are running has such a
-command (or you can specify a specific command using the
-``status`` parameter).
-
-If you do not specify anything, then the service name will be
-looked for in the process table.
-
-#### name (*namevar*)
-The name of the service to run. This name
-is used to find the service in whatever service subsystem it
-is in.
-
-#### path
-The search path for finding init scripts.
-
-#### pattern
-The pattern to search for in the process table.
-This is used for stopping services on platforms that do not
-support init scripts, and is also used for determining service
-status on those service whose init scripts do not include a status
-command.
-
-If this is left unspecified and is needed to check the status
-of a service, then the service name will be used instead.
-
-The pattern can be a simple string or any legal Ruby pattern.
-
-#### provider
-The specific backend for provider to use. You will
-seldom need to specify this -- Puppet will usually discover the
-appropriate provider for your platform. Available providers are:
-
-* **base**: The simplest form of service support. You have to specify
- enough about your service for this to work; the minimum you can specify
- is a binary for starting the process, and this same binary will be searched
- for in the process table to stop the service. It is preferable to
- specify start, stop, and status commands, akin to how you would do
- so using ``init``. Required binaries: ``kill``.
-* **debian**: Debian's form of ``init``-style management. The only difference
- is that this supports service enabling and disabling via ``update-rc.d``. Default for ``operatingsystem`` == ``debian``. Required binaries: ``/usr/sbin/update-rc.d``.
-* **gentoo**: Gentoo's form of ``init``-style service
- management; uses ``rc-update`` for service enabling and disabling. Default for ``operatingsystem`` == ``gentoo``. Required binaries: ``/sbin/rc-update``.
-* **init**: Standard init service management. This provider assumes that the
- init script has not ``status`` command, because so few scripts do,
- so you need to either provide a status command or specify via ``hasstatus``
- that one already exists in the init script.
-* **redhat**: Red Hat's (and probably many others) form of ``init``-style service
- management; uses ``chkconfig`` for service enabling and disabling. Default for ``operatingsystem`` == ``redhatfedorasuse``. Required binaries: ``/sbin/chkconfig``.
-* **smf**: Support for Sun's new Service Management Framework. Starting a service
- is effectively equivalent to enabling it, so there is only support
- for starting and stopping services, which also enables and disables them,
- respectively. Default for ``operatingsystem`` == ``solaris``. Required binaries: ``/usr/bin/svcs``, ``/usr/sbin/svcadm``.
-
-#### restart
-Specify a *restart* command manually. If left
-unspecified, the service will be stopped and then started.
-
-#### running
-A place-holder parameter that wraps ``ensure``, because
-``running`` is deprecated. You should use ``ensure`` instead
-of this, but using this will still work, albeit with a
-warning.
-
-#### start
-Specify a *start* command manually. Most service subsystems
-support a ``start`` command, so this will not need to be
-specified.
-
-#### status
-Specify a *status* command manually. If left
-unspecified, the status method will be determined
-automatically, usually by looking for the service in the
-process table.
-
-#### stop
-Specify a *stop* command manually.
-
-#### type
-Deprecated form of ``provder``.
-
-
-
-
-----------------
-
-
-<h2><a name='sshkey'>sshkey</a></h2>
-Installs and manages ssh host keys. At this point, this type
-only knows how to install keys into /etc/ssh/ssh_known_hosts, and
-it cannot manage user authorized keys yet.
-
-
-### Sshkey Parameters
-#### alias
-Any alias the host might have. Multiple values must be
-specified as an array. Note that this state has the same name
-as one of the metaparams; using this state to set aliases will
-make those aliases available in your Puppet scripts.
-
-#### ensure
-The basic state that the object should be in. Valid values are ``absent``, ``present``.
-
-#### key
-The key itself; generally a long string of hex digits.
-
-#### name (*namevar*)
-The host name.
-
-#### provider
-The specific backend for provider to use. You will
-seldom need to specify this -- Puppet will usually discover the
-appropriate provider for your platform. Available providers are:
-
-* **parsed**:
-
-#### target
-The file in which to store the mount table. Only used by
-those providers that write to disk (i.e., not NetInfo).
-
-#### type
-The encryption type used. Probably ssh-dss or ssh-rsa. Valid values are ``ssh-dss`` (also called ``dsa``), ``ssh-rsa`` (also called ``rsa``).
-
-
-
-
-----------------
-
-
-<h2><a name='tidy'>tidy</a></h2>
-Remove unwanted files based on specific criteria. Multiple
-criteria are OR'd together, so a file that is too large but is not
-old enough will still get tidied.
-
-
-### Tidy Parameters
-#### age
-Tidy files whose age is equal to or greater than
-the specified time. You can choose seconds, minutes,
-hours, days, or weeks by specifying the first letter of any
-of those words (e.g., '1w').
-
-#### backup
-Whether files should be backed up before
-being replaced. The preferred method of backing files up is via
-a ``filebucket``, which stores files by their MD5 sums and allows
-easy retrieval without littering directories with backups. You
-can specify a local filebucket or a network-accessible
-server-based filebucket. Alternatively, if you specify any
-value that begins with a ``.`` (e.g., ``.puppet-bak``), then
-Puppet will use copy the file in the same directory with that
-value as the extension of the backup.
-
-Puppet automatically creates a local filebucket named ``puppet`` and
-defaults to backing up there. To use a server-based filebucket,
-you must specify one in your configuration:
-
- filebucket { main:
- server => puppet
- }
-
-The ``puppetmasterd`` daemon creates a filebucket by default,
-so you can usually back up to your main server with this
-configuration. Once you've described the bucket in your
-configuration, you can use it in any file:
-
- file { "/my/file":
- source => "/path/in/nfs/or/something",
- backup => main
- }
-
-This will back the file up to the central server.
-
-At this point, the benefits of using a filebucket are that you do not
-have backup files lying around on each of your machines, a given
-version of a file is only backed up once, and you can restore
-any given file manually, no matter how old. Eventually,
-transactional support will be able to automatically restore
-filebucketed files.
-
-#### path (*namevar*)
-The path to the file or directory to manage. Must be fully
-qualified.
-
-#### recurse
-If target is a directory, recursively descend
-into the directory looking for files to tidy.
-
-#### rmdirs
-Tidy directories in addition to files; that is, remove
-directories whose age is older than the specified criteria.
-This will only remove empty directories, so all contained
-files must also be tidied before a directory gets removed.
-
-#### size
-Tidy files whose size is equal to or greater than
-the specified size. Unqualified values are in kilobytes, but
-*b*, *k*, and *m* can be appended to specify *bytes*, *kilobytes*,
-and *megabytes*, respectively. Only the first character is
-significant, so the full word can also be used.
-
-#### type
-Set the mechanism for determining age. Valid values are ``atime``, ``mtime``, ``ctime``.
-
-
-
-
-----------------
-
-
-<h2><a name='user'>user</a></h2>
-Manage users. Currently can create and modify users, but
-cannot delete them. Theoretically all of the parameters are
-optional, but if no parameters are specified the comment will
-be set to the user name in order to make the internals work out
-correctly.
-
-This element type uses the prescribed native tools for creating
-groups and generally uses POSIX APIs for retrieving information
-about them. It does not directly modify /etc/passwd or anything.
-
-For most platforms, the tools used are ``useradd`` and its ilk;
-for Mac OS X, NetInfo is used. This is currently unconfigurable,
-but if you desperately need it to be so, please contact us.
-
-
-### User Parameters
-#### allowdupe
-Whether to allow duplicate UIDs. Valid values are ``true``, ``false``.
-
-#### comment
-A description of the user. Generally is a user's full name.
-
-#### ensure
-The basic state that the object should be in. Valid values are ``absent``, ``present``.
-
-#### gid
-The user's primary group. Can be specified numerically or
-by name.
-
-#### groups
-The groups of which the user is a member. The primary
-group should not be listed. Multiple groups should be
-specified as an array.
-
-#### home
-The home directory of the user. The directory must be created
-separately and is not currently checked for existence.
-
-#### membership
-Whether specified groups should be treated as the only groups
-of which the user is a member or whether they should merely
-be treated as the minimum membership list. Valid values are ``inclusive``, ``minimum``.
-
-#### name (*namevar*)
-User name. While limitations are determined for
-each operating system, it is generally a good idea to keep to
-the degenerate 8 characters, beginning with a letter.
-
-#### provider
-The specific backend for provider to use. You will
-seldom need to specify this -- Puppet will usually discover the
-appropriate provider for your platform. Available providers are:
-
-* **netinfo**: User management in NetInfo. Default for ``operatingsystem`` == ``darwin``. Required binaries: ``nireport``, ``niutil``.
-* **pw**: User management via ``pw`` on FreeBSD. Default for ``operatingsystem`` == ``freebsd``. Required binaries: ``pw``.
-* **useradd**: User management via ``useradd`` and its ilk. Required binaries: ``useradd``, ``usermod``, ``userdel``.
-
-#### shell
-The user's login shell. The shell must exist and be
-executable.
-
-#### uid
-The user ID. Must be specified numerically. For new users
-being created, if no user ID is specified then one will be
-chosen automatically, which will likely result in the same user
-having different IDs on different systems, which is not
-recommended.
-
-
-
-
-----------------
-
-
-<h2><a name='yumrepo'>yumrepo</a></h2>
-The client-side description of a yum repository. Repository
-configurations are found by parsing /etc/yum.conf and
-the files indicated by reposdir in that file (see yum.conf(5)
-for details)
-
-Most parameters are identical to the ones documented
-in yum.conf(5)
-
-Continuation lines that yum supports for example for the
-baseurl are not supported. No attempt is made to access
-files included with the **include** directive
-
-
-### Yumrepo Parameters
-#### baseurl
-The URL for this repository.
-Set this to 'absent' to remove it from the file completely Valid values are ``absent``. Values can also match ``(?-mix:.*)``.
-
-#### descr
-A human readable description of the repository.
-Set this to 'absent' to remove it from the file completely Valid values are ``absent``. Values can also match ``(?-mix:.*)``.
-
-#### enabled
-Whether this repository is enabled or disabled. Possible
-values are '0', and '1'.
-Set this to 'absent' to remove it from the file completely Valid values are ``absent``. Values can also match ``(?-mix:(0|1))``.
-
-#### enablegroups
-Determines whether yum will allow the use of
-package groups for this repository. Possible
-values are '0', and '1'.
-Set this to 'absent' to remove it from the file completely Valid values are ``absent``. Values can also match ``(?-mix:(0|1))``.
-
-#### exclude
-List of shell globs. Matching packages will never be
-considered in updates or installs for this repo.
-Set this to 'absent' to remove it from the file completely Valid values are ``absent``. Values can also match ``(?-mix:.*)``.
-
-#### failovermethod
-Either 'roundrobin' or 'priority'.
-Set this to 'absent' to remove it from the file completely Valid values are ``absent``. Values can also match ``(?-mix:roundrobin|priority)``.
-
-#### gpgcheck
-Whether to check the GPG signature on packages installed
-from this repository. Possible values are '0', and '1'.
-
-Set this to 'absent' to remove it from the file completely Valid values are ``absent``. Values can also match ``(?-mix:(0|1))``.
-
-#### gpgkey
-The URL for the GPG key with which packages from this
-repository are signed.
-Set this to 'absent' to remove it from the file completely Valid values are ``absent``. Values can also match ``(?-mix:.*)``.
-
-#### include
-A URL from which to include the config.
-Set this to 'absent' to remove it from the file completely Valid values are ``absent``. Values can also match ``(?-mix:.*)``.
-
-#### includepkgs
-List of shell globs. If this is set, only packages
-matching one of the globs will be considered for
-update or install.
-Set this to 'absent' to remove it from the file completely Valid values are ``absent``. Values can also match ``(?-mix:.*)``.
-
-#### keepalive
-Either '1' or '0'. This tells yum whether or not HTTP/1.1
-keepalive should be used with this repository.
-Set this to 'absent' to remove it from the file completely Valid values are ``absent``. Values can also match ``(?-mix:(0|1))``.
-
-#### metadata_expire
-Number of seconds after which the metadata will expire.
-Set this to 'absent' to remove it from the file completely Valid values are ``absent``. Values can also match ``(?-mix:[0-9]+)``.
-
-#### mirrorlist
-The URL that holds the list of mirrors for this repository.
-Set this to 'absent' to remove it from the file completely Valid values are ``absent``. Values can also match ``(?-mix:.*)``.
-
-#### name (*namevar*)
-The name of the repository.
-
-#### timeout
-Number of seconds to wait for a connection before timing
-out.
-Set this to 'absent' to remove it from the file completely Valid values are ``absent``. Values can also match ``(?-mix:[0-9]+)``.
-
-
-
-
-----------------
-
-
-<h2><a name='zone'>zone</a></h2>
-Solaris zones.
-
-
-### Zone Parameters
-#### autoboot
-Whether the zone should automatically boot. Valid values are ``true``, ``false``.
-
-#### ensure
-The running state of the zone. The valid states directly reflect
-the states that ``zoneadm`` provides. The states are linear,
-in that a zone must be ``configured`` then ``installed``, and
-only then can be ``running``. Note also that ``halt`` is currently
-used to stop zones. Valid values are ``absent``, ``configured``, ``installed``, ``running``.
-
-#### id
-The numerical ID of the zone. This number is autogenerated
-and cannot be changed.
-
-#### inherit
-The list of directories that the zone inherits from the global
-zone. All directories must be fully qualified.
-
-#### ip
-The IP address of the zone. IP addresses must be specified
-with the interface, separated by a colon, e.g.: bge0:192.168.0.1.
-For multiple interfaces, specify them in an array.
-
-#### name (*namevar*)
-The name of the zone.
-
-#### path
-The root of the zone's filesystem. Must be a fully qualified
-file name. If you include '%s' in the path, then it will be
-replaced with the zone's name. At this point, you cannot use
-Puppet to move a zone.
-
-#### pool
-The resource pool for this zone.
-
-#### provider
-The specific backend for provider to use. You will
-seldom need to specify this -- Puppet will usually discover the
-appropriate provider for your platform. Available providers are:
-
-* **solaris**: Provider for Solaris Zones. Default for ``operatingsystem`` == ``solaris``. Required binaries: ``/usr/sbin/zoneadm``, ``/usr/sbin/zonecfg``.
-
-#### realhostname
-The actual hostname of the zone.
-
-#### shares
-Number of FSS CPU shares allocated to the zone.
-
-#### sysidcfg
-The text to go into the sysidcfg file when the zone is first
-booted. The best way is to use a template:
-
-<pre><code>
- # $templatedir/sysidcfg
- system_locale=en_US
- timezone=GMT
- terminal=xterms
- security_policy=NONE
- root_password=&lt;%= password %>
- timeserver=localhost
- name_service=DNS {domain_name=&lt;%= domain %>
- name_server=&lt;%= nameserver %>}
- network_interface=primary {hostname=&lt;%= realhostname %>
- ip_address=&lt;%= ip %>
- netmask=&lt;%= netmask %>
- protocol_ipv6=no
- default_route=&lt;%= defaultroute %>}
- nfs4_domain=dynamic
-</code></pre>
-
-And then call that:
-
- zone { myzone:
- ip => "bge0:192.168.0.23",
- sysidcfg => template(sysidcfg),
- path => "/opt/zones/myzone",
- realhostname => "fully.qualified.domain.name"
- }
-
-The sysidcfg only matters on the first booting of the zone,
-so Puppet only checks for it at that time.
-
-
-
-
-
-----------------
-
-
-*This page autogenerated on Fri Jan 26 16:40:46 CST 2007*
diff --git a/lib/puppet.rb b/lib/puppet.rb
index b11f81d49..b9a09bb49 100644
--- a/lib/puppet.rb
+++ b/lib/puppet.rb
@@ -115,7 +115,7 @@ module Puppet
end
# Load all of the configuration parameters.
- require 'puppet/configuration'
+ require 'puppet/defaults'
def self.genconfig
if Puppet[:configprint] != ""
diff --git a/lib/puppet/configuration.rb b/lib/puppet/defaults.rb
index a8f6e0c35..78364e786 100644
--- a/lib/puppet/configuration.rb
+++ b/lib/puppet/defaults.rb
@@ -126,7 +126,12 @@ module Puppet
:environment => ["", "The environment Puppet is running in. For clients (e.g., ``puppetd``) this
determines the environment itself, which is used to find modules and much more. For
servers (i.e., ``puppetmasterd``) this provides the default environment for nodes we
- know nothing about."]
+ know nothing about."],
+ :diff_args => ["", "Which arguments to pass to the diff command when printing differences between files."],
+ :diff => ["diff", "Which diff command to use when printing differences between files."],
+ :show_diff => [false, "Whether to print a contextual diff when files are being replaced. The diff
+ is printed on stdout, so this option is meaningless unless you are running Puppet interactively.
+ This feature currently requires the ``diff/lcs`` Ruby library."]
)
hostname = Facter["hostname"].value
diff --git a/lib/puppet/dsl.rb b/lib/puppet/dsl.rb
index 9c652f082..793578bca 100644
--- a/lib/puppet/dsl.rb
+++ b/lib/puppet/dsl.rb
@@ -113,10 +113,6 @@ module Puppet
@aspects = {}
- # For now, just do some hackery so resources work
- @@interp = Puppet::Parser::Interpreter.new :Code => ""
- @@scope = Puppet::Parser::Scope.new(:interp => @@interp)
-
@@objects = Hash.new do |hash, key|
hash[key] = {}
end
@@ -237,7 +233,7 @@ module Puppet
end
unless obj = @@objects[type][name]
obj = Resource.new :title => name, :type => type.name,
- :source => source, :scope => @@scope
+ :source => source, :scope => scope
@@objects[type][name] = obj
@resources << obj
@@ -250,12 +246,26 @@ module Puppet
:source => source
)
- obj.set(param)
+ obj.send(:set_parameter, param)
end
obj
end
+ def scope
+ unless defined?(@scope)
+ @interp = Puppet::Parser::Interpreter.new :Code => ""
+ # Load the class, so the node object class is available.
+ require 'puppet/network/handler/node'
+ @node = Puppet::Node.new(Facter.value(:hostname))
+ @node.parameters = Facter.to_hash
+ @interp = Puppet::Parser::Interpreter.new :Code => ""
+ @compile = Puppet::Parser::Compile.new(@node, @interp.send(:parser, Puppet[:environment]))
+ @scope = @compile.topscope
+ end
+ @scope
+ end
+
def type
self.name
end
diff --git a/lib/puppet/error.rb b/lib/puppet/error.rb
index 10ccf47be..55fbf2e2f 100644
--- a/lib/puppet/error.rb
+++ b/lib/puppet/error.rb
@@ -50,5 +50,3 @@ module Puppet # :nodoc:
end
end
end
-
-# $Id$
diff --git a/lib/puppet/fact_stores/yaml.rb b/lib/puppet/fact_stores/yaml.rb
index b4b0c0e7c..a4b12a2e5 100644
--- a/lib/puppet/fact_stores/yaml.rb
+++ b/lib/puppet/fact_stores/yaml.rb
@@ -38,5 +38,3 @@ Puppet::Util::FactStore.newstore(:yaml) do
File.join(Puppet[:yamlfactdir], node + ".yaml")
end
end
-
-# $Id$
diff --git a/lib/puppet/feature/base.rb b/lib/puppet/feature/base.rb
index 97200cb39..6368d0931 100644
--- a/lib/puppet/feature/base.rb
+++ b/lib/puppet/feature/base.rb
@@ -20,4 +20,5 @@ Puppet.features.add(:root) { require 'puppet/util/suidmanager'; Puppet::Util::SU
# We've got mongrel available
Puppet.features.add(:mongrel, :libs => %w{rubygems mongrel puppet/network/server/mongrel})
-# $Id$
+# We have lcs diff
+Puppet.features.add :diff, :libs => %w{diff/lcs diff/lcs/hunk}
diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb
new file mode 100644
index 000000000..0ba538355
--- /dev/null
+++ b/lib/puppet/indirector.rb
@@ -0,0 +1,76 @@
+# Manage indirections to termini. They are organized in terms of indirections -
+# - e.g., configuration, node, file, certificate -- and each indirection has one
+# or more terminus types defined. The indirection must have its preferred terminus
+# configured via a 'default' in the form of '<indirection>_terminus'; e.g.,
+# 'node_terminus = ldap'.
+module Puppet::Indirector
+ # This manages reading in all of our files for us and then retrieving
+ # loaded instances. We still have to define the 'newX' method, but this
+ # does all of the rest -- loading, storing, and retrieving by name.
+ require 'puppet/util/instance_loader'
+ include Puppet::Util::InstanceLoader
+
+ # Define a new indirection terminus. This method is used by the individual
+ # termini in their separate files. Again, the autoloader takes care of
+ # actually loading these files.
+ def register_terminus(name, options = {}, &block)
+ genclass(name, :hash => instance_hash(indirection.name), :attributes => options, :block => block)
+ end
+
+ # Retrieve a terminus class by indirection and name.
+ def terminus(name)
+ loaded_instance(name)
+ end
+
+ # Declare that the including class indirects its methods to
+ # this terminus. The terminus name must be the name of a Puppet
+ # default, not the value -- if it's the value, then it gets
+ # evaluated at parse time, which is before the user has had a chance
+ # to override it.
+ def indirects(indirection, options)
+ @indirection = indirection
+ @indirect_terminus = options[:to]
+
+ # Set up autoloading of the appropriate termini.
+ autoload "puppet/indirector/%s" % indirection
+ end
+
+ # Define methods for each of the HTTP methods. These just point to the
+ # termini, with consistent error-handling. Each method is called with
+ # the first argument being the indirection type and the rest of the
+ # arguments passed directly on to the indirection terminus. There is
+ # currently no attempt to standardize around what the rest of the arguments
+ # should allow or include or whatever.
+ # There is also no attempt to pre-validate that a given indirection supports
+ # the method in question. We should probably require that indirections
+ # declare supported methods, and then verify that termini implement all of
+ # those methods.
+ [:get, :post, :put, :delete].each do |method_name|
+ define_method(method_name) do |*args|
+ begin
+ terminus.send(method_name, *args)
+ rescue NoMethodError
+ raise ArgumentError, "Indirection category %s does not respond to REST method %s" % [indirection, method_name]
+ end
+ end
+ end
+
+ private
+
+ # Create a new terminus instance.
+ def make_terminus(indirection)
+ # Load our terminus class.
+ unless klass = self.class.terminus(indirection, indirection.default)
+ raise ArgumentError, "Could not find terminus %s for indirection %s" % [indirection.default, indirection]
+ end
+ return klass.new
+ end
+
+ # Return the singleton terminus for this indirection.
+ def terminus
+ unless terminus = @termini[indirection.name]
+ terminus = @termini[indirection.name] = make_terminus(indirection)
+ end
+ terminus
+ end
+end
diff --git a/lib/puppet/metatype/evaluation.rb b/lib/puppet/metatype/evaluation.rb
index 4b8d293c4..b358fefad 100644
--- a/lib/puppet/metatype/evaluation.rb
+++ b/lib/puppet/metatype/evaluation.rb
@@ -29,7 +29,16 @@ class Puppet::Type
self.debug "%s change(s)" %
[changes.length]
end
- self.cache(:checked, Time.now)
+
+ # If we're in noop mode, we don't want to store the checked time,
+ # because it will result in the resource not getting scheduled if
+ # someone were to run the configuration in non-noop mode.
+ # We're going to go ahead and record that we checked if there were
+ # no changes, since it's unlikely it will affect the scheduling.
+ noop = noop?
+ if ! noop or (noop && changes.length == 0)
+ self.cache(:checked, Time.now)
+ end
return changes.flatten
end
diff --git a/lib/puppet/metatype/metaparams.rb b/lib/puppet/metatype/metaparams.rb
index 9825efc61..92f12e6cb 100644
--- a/lib/puppet/metatype/metaparams.rb
+++ b/lib/puppet/metatype/metaparams.rb
@@ -273,8 +273,8 @@ class Puppet::Type
# Either of the two retrieval attempts could have returned
# nil.
unless object
- self.fail "Could not retrieve dependency '%s[%s]'" %
- [tname.to_s.capitalize, name]
+ self.fail "Could not retrieve dependency '%s[%s]' of %s" %
+ [tname.to_s.capitalize, @resource.ref, name]
end
# Are we requiring them, or vice versa? See the method docs
diff --git a/lib/puppet/metatype/providers.rb b/lib/puppet/metatype/providers.rb
index 8210dd382..c302d9928 100644
--- a/lib/puppet/metatype/providers.rb
+++ b/lib/puppet/metatype/providers.rb
@@ -230,6 +230,9 @@ class Puppet::Type
# Return an array of all of the suitable providers.
def self.suitableprovider
+ if @providers.empty?
+ providerloader.loadall
+ end
@providers.find_all { |name, provider|
provider.suitable?
}.collect { |name, provider|
@@ -249,5 +252,3 @@ class Puppet::Type
end
end
end
-
-# $Id$
diff --git a/lib/puppet/module.rb b/lib/puppet/module.rb
index 87f849d17..924958bbe 100644
--- a/lib/puppet/module.rb
+++ b/lib/puppet/module.rb
@@ -8,10 +8,11 @@ class Puppet::Module
# Return an array of paths by splitting the +modulepath+ config
# parameter. Only consider paths that are absolute and existing
# directories
- def self.modulepath
- dirs = Puppet[:modulepath].split(":")
+ def self.modulepath(environment = nil)
+ dirs = Puppet.config.value(:modulepath, environment).split(":")
if ENV["PUPPETLIB"]
dirs = ENV["PUPPETLIB"].split(":") + dirs
+ else
end
dirs.select do |p|
p =~ /^#{File::SEPARATOR}/ && File::directory?(p)
@@ -21,15 +22,12 @@ class Puppet::Module
# Find and return the +module+ that +path+ belongs to. If +path+ is
# absolute, or if there is no module whose name is the first component
# of +path+, return +nil+
- def self.find(path)
- if path =~ %r/^#{File::SEPARATOR}/
+ def self.find(modname, environment = nil)
+ if modname =~ %r/^#{File::SEPARATOR}/
return nil
end
- modname, rest = path.split(File::SEPARATOR, 2)
- return nil if modname.nil? || modname.empty?
-
- modpath = modulepath.collect { |p|
+ modpath = modulepath(environment).collect { |p|
File::join(p, modname)
}.find { |f| File::directory?(f) }
return nil unless modpath
@@ -45,16 +43,25 @@ class Puppet::Module
# param.
# In all cases, an absolute path is returned, which does not
# necessarily refer to an existing file
- def self.find_template(file)
- if file =~ /^#{File::SEPARATOR}/
- return file
+ def self.find_template(template, environment = nil)
+ if template =~ /^#{File::SEPARATOR}/
+ return template
end
- mod = find(file)
+ path, file = split_path(template)
+
+ # Because templates don't have an assumed template name, like manifests do,
+ # we treat templates with no name as being templates in the main template
+ # directory.
+ if file.nil?
+ mod = nil
+ else
+ mod = find(path, environment)
+ end
if mod
return mod.template(file)
else
- return File.join(Puppet[:templatedir], file)
+ return File.join(Puppet.config.value(:templatedir, environment), template)
end
end
@@ -63,13 +70,14 @@ class Puppet::Module
# +pat+ does not contain any wildcards and is an existing module, return
# a list of manifests in that module matching the rest of +pat+
# Otherwise, try to find manifests matching +pat+ relative to +cwd+
- def self.find_manifests(pat, cwd = nil)
- cwd ||= Dir.getwd
- mod = find(pat)
+ def self.find_manifests(start, options = {})
+ cwd = options[:cwd] || Dir.getwd
+ path, pat = split_path(start)
+ mod = find(path, options[:environment])
if mod
return mod.manifests(pat)
else
- abspat = File::expand_path(pat, cwd)
+ abspat = File::expand_path(start, cwd)
files = Dir.glob(abspat).reject { |f| FileTest.directory?(f) }
if files.size == 0
files = Dir.glob(abspat + ".pp").reject { |f| FileTest.directory?(f) }
@@ -78,28 +86,34 @@ class Puppet::Module
end
end
+ # Split the path into the module and the rest of the path.
+ def self.split_path(path)
+ if path =~ %r/^#{File::SEPARATOR}/
+ return nil
+ end
+
+ modname, rest = path.split(File::SEPARATOR, 2)
+ return nil if modname.nil? || modname.empty?
+ return modname, rest
+ end
+
attr_reader :name, :path
def initialize(name, path)
@name = name
@path = path
end
- def strip(file)
- n, rest = file.split(File::SEPARATOR, 2)
- rest = nil if rest && rest.empty?
- return rest
- end
-
def template(file)
- return File::join(path, TEMPLATES, strip(file))
+ return File::join(path, TEMPLATES, file)
end
def files
return File::join(path, FILES)
end
- def manifests(pat)
- rest = strip(pat)
+ # Return the list of manifests matching the given glob pattern,
+ # defaulting to 'init.pp' for empty modules.
+ def manifests(rest)
rest ||= "init.pp"
p = File::join(path, MANIFESTS, rest)
files = Dir.glob(p)
@@ -111,5 +125,3 @@ class Puppet::Module
private :initialize
end
-
-# $Id$
diff --git a/lib/puppet/network/client/master.rb b/lib/puppet/network/client/master.rb
index 41fd0f7f2..c6d7cd75d 100644
--- a/lib/puppet/network/client/master.rb
+++ b/lib/puppet/network/client/master.rb
@@ -38,6 +38,9 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
# in their manifests
facts["clientversion"] = Puppet.version.to_s
+ # And add our environment as a fact.
+ facts["environment"] = Puppet[:environment]
+
facts
end
@@ -149,12 +152,12 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
return false
end
- # We're willing to give a 2 second drift
newcompile = @driver.freshness
+ # We're willing to give a 2 second drift
if newcompile - @compile_time.to_i < 1
return true
else
- Puppet.debug "Server compile time is %s vs %s" % [newcompile, @compile_time]
+ Puppet.debug "Server compile time is %s vs %s" % [newcompile, @compile_time.to_i]
return false
end
end
@@ -691,5 +694,3 @@ class Puppet::Network::Client::Master < Puppet::Network::Client
sleep(time)
end
end
-
-# $Id$
diff --git a/lib/puppet/network/handler.rb b/lib/puppet/network/handler.rb
index 33343e4fe..c2fbfcba5 100644
--- a/lib/puppet/network/handler.rb
+++ b/lib/puppet/network/handler.rb
@@ -10,7 +10,7 @@ module Puppet::Network
# This is so that the handlers can subclass just 'Handler', rather
# then having to specify the full class path.
Handler = self
- attr_accessor :server
+ attr_accessor :server, :local
extend Puppet::Util::SubclassLoader
extend Puppet::Util
@@ -44,6 +44,10 @@ module Puppet::Network
# Create an empty init method with the same signature.
def initialize(hash = {})
end
+
+ def local?
+ self.local
+ end
end
end
diff --git a/lib/puppet/network/handler/configuration.rb b/lib/puppet/network/handler/configuration.rb
index 7f81879ba..2c72d3d2b 100644
--- a/lib/puppet/network/handler/configuration.rb
+++ b/lib/puppet/network/handler/configuration.rb
@@ -8,219 +8,213 @@ require 'yaml'
class Puppet::Network::Handler
class Configuration < Handler
desc "Puppet's configuration compilation interface. Passed a node name
- or other key, retrieves information about the node and returns a
- compiled configuration."
+ or other key, retrieves information about the node (using the ``node_source``)
+ and returns a compiled configuration."
include Puppet::Util
- attr_accessor :ast, :local
- attr_reader :ca
+ attr_accessor :local
@interface = XMLRPC::Service::Interface.new("configuration") { |iface|
iface.add_method("string configuration(string)")
iface.add_method("string version()")
}
- # FIXME At some point, this should be autodocumenting.
- def addfacts(facts)
- # Add our server version to the fact list
- facts["serverversion"] = Puppet.version.to_s
-
- # And then add the server name and IP
- {"servername" => "fqdn",
- "serverip" => "ipaddress"
- }.each do |var, fact|
- if obj = Facter[fact]
- facts[var] = obj.value
- else
- Puppet.warning "Could not retrieve fact %s" % fact
- end
+ # Compile a node's configuration.
+ def configuration(key, client = nil, clientip = nil)
+ # If we want to use the cert name as our key
+ if Puppet[:node_name] == 'cert' and client
+ key = client
end
- if facts["servername"].nil?
- host = Facter.value(:hostname)
- if domain = Facter.value(:domain)
- facts["servername"] = [host, domain].join(".")
- else
- facts["servername"] = host
- end
+ # Note that this is reasonable, because either their node source should actually
+ # know about the node, or they should be using the ``none`` node source, which
+ # will always return data.
+ unless node = node_handler.details(key)
+ raise Puppet::Error, "Could not find node '%s'" % key
end
+
+ # Add any external data to the node.
+ add_node_data(node)
+
+ configuration = compile(node)
+
+ return translate(configuration)
end
- # Manipulate the client name as appropriate.
- def clientname(name, ip, facts)
- # Always use the hostname from Facter.
- client = facts["hostname"]
- clientip = facts["ipaddress"]
- if Puppet[:node_name] == 'cert'
- if name
- client = name
- end
- if ip
- clientip = ip
- end
+ def initialize(options = {})
+ if options[:Local]
+ @local = options[:Local]
+ else
+ @local = false
end
- return client, clientip
+ # Just store the options, rather than creating the interpreter
+ # immediately. Mostly, this is so we can create the interpreter
+ # on-demand, which is easier for testing.
+ @options = options
+
+ set_server_facts
end
- # Tell a client whether there's a fresh config for it
- def freshness(client = nil, clientip = nil)
- if Puppet.features.rails? and Puppet[:storeconfigs]
- Puppet::Rails.connect
+ # Are we running locally, or are our clients networked?
+ def local?
+ self.local
+ end
- host = Puppet::Rails::Host.find_or_create_by_name(client)
- host.last_freshcheck = Time.now
- if clientip and (! host.ip or host.ip == "" or host.ip == "NULL")
- host.ip = clientip
- end
- host.save
- end
- if defined? @interpreter
- return @interpreter.parsedate
+ # Return the configuration version.
+ def version(client = nil, clientip = nil)
+ if client and node = node_handler.details(client)
+ update_node_check(node)
+ return interpreter.configuration_version(node)
else
- return 0
+ # Just return something that will always result in a recompile, because
+ # this is local.
+ return (Time.now + 1000).to_i
end
end
- def initialize(hash = {})
- args = {}
+ private
- # Allow specification of a code snippet or of a file
- if code = hash[:Code]
- args[:Code] = code
- else
- args[:Manifest] = hash[:Manifest] || Puppet[:manifest]
+ # Add any extra data necessary to the node.
+ def add_node_data(node)
+ # Merge in our server-side facts, so they can be used during compilation.
+ node.fact_merge(@server_facts)
+
+ # Add any specified classes to the node's class list.
+ if classes = @options[:Classes]
+ classes.each do |klass|
+ node.classes << klass
+ end
end
+ end
- if hash[:Local]
- @local = hash[:Local]
+ # Compile the actual configuration.
+ def compile(node)
+ # Pick the benchmark level.
+ if local?
+ level = :none
else
- @local = false
+ level = :notice
end
- args[:Local] = @local
+ # Ask the interpreter to compile the configuration.
+ str = "Compiled configuration for %s" % node.name
+ if node.environment
+ str += " in environment %s" % node.environment
+ end
+ config = nil
+ benchmark(level, "Compiled configuration for %s" % node.name) do
+ begin
+ config = interpreter.compile(node)
+ rescue Puppet::Error => detail
+ if Puppet[:trace]
+ puts detail.backtrace
+ end
+ unless local?
+ Puppet.err detail.to_s
+ end
+ raise XMLRPC::FaultException.new(
+ 1, detail.to_s
+ )
+ end
+ end
- if hash.include?(:CA) and hash[:CA]
- @ca = Puppet::SSLCertificates::CA.new()
- else
- @ca = nil
+ return config
+ end
+
+ # Create our interpreter object.
+ def create_interpreter(options)
+ args = {}
+
+ # Allow specification of a code snippet or of a file
+ if code = options[:Code]
+ args[:Code] = code
+ elsif options[:Manifest]
+ args[:Manifest] = options[:Manifest]
end
- Puppet.debug("Creating interpreter")
+ args[:Local] = local?
- if hash.include?(:UseNodes)
- args[:UseNodes] = hash[:UseNodes]
+ if options.include?(:UseNodes)
+ args[:UseNodes] = options[:UseNodes]
elsif @local
args[:UseNodes] = false
end
# This is only used by the cfengine module, or if --loadclasses was
# specified in +puppet+.
- if hash.include?(:Classes)
- args[:Classes] = hash[:Classes]
+ if options.include?(:Classes)
+ args[:Classes] = options[:Classes]
end
- @interpreter = Puppet::Parser::Interpreter.new(args)
+ return Puppet::Parser::Interpreter.new(args)
end
- def getconfig(facts, format = "marshal", client = nil, clientip = nil)
- if @local
- # we don't need to do anything, since we should already
- # have raw objects
- Puppet.debug "Our client is local"
- else
- Puppet.debug "Our client is remote"
-
- # XXX this should definitely be done in the protocol, somehow
- case format
- when "marshal":
- Puppet.warning "You should upgrade your client. 'Marshal' will not be supported much longer."
- begin
- facts = Marshal::load(CGI.unescape(facts))
- rescue => detail
- raise XMLRPC::FaultException.new(
- 1, "Could not rebuild facts"
- )
- end
- when "yaml":
- begin
- facts = YAML.load(CGI.unescape(facts))
- rescue => detail
- raise XMLRPC::FaultException.new(
- 1, "Could not rebuild facts"
- )
- end
- else
- raise XMLRPC::FaultException.new(
- 1, "Unavailable config format %s" % format
- )
- end
+ # Create/return our interpreter.
+ def interpreter
+ unless defined?(@interpreter) and @interpreter
+ @interpreter = create_interpreter(@options)
end
+ @interpreter
+ end
- client, clientip = clientname(client, clientip, facts)
+ # Create a node handler instance for looking up our nodes.
+ def node_handler
+ unless defined?(@node_handler)
+ @node_handler = Puppet::Network::Handler.handler(:node).create
+ end
+ @node_handler
+ end
- # Add any server-side facts to our server.
- addfacts(facts)
+ # Initialize our server fact hash; we add these to each client, and they
+ # won't change while we're running, so it's safe to cache the values.
+ def set_server_facts
+ @server_facts = {}
- retobjects = nil
+ # Add our server version to the fact list
+ @server_facts["serverversion"] = Puppet.version.to_s
- # This is hackish, but there's no "silence" option for benchmarks
- # right now
- if @local
- #begin
- retobjects = @interpreter.run(client, facts)
- #rescue Puppet::Error => detail
- # Puppet.err detail
- # raise XMLRPC::FaultException.new(
- # 1, detail.to_s
- # )
- #rescue => detail
- # Puppet.err detail.to_s
- # return ""
- #end
- else
- benchmark(:notice, "Compiled configuration for %s" % client) do
- begin
- retobjects = @interpreter.run(client, facts)
- rescue Puppet::Error => detail
- Puppet.err detail
- raise XMLRPC::FaultException.new(
- 1, detail.to_s
- )
- rescue => detail
- Puppet.err detail.to_s
- return ""
- end
+ # And then add the server name and IP
+ {"servername" => "fqdn",
+ "serverip" => "ipaddress"
+ }.each do |var, fact|
+ if value = Facter.value(fact)
+ @server_facts[var] = value
+ else
+ Puppet.warning "Could not retrieve fact %s" % fact
end
end
- if @local
- return retobjects
- else
- str = nil
- case format
- when "marshal":
- str = Marshal::dump(retobjects)
- when "yaml":
- str = retobjects.to_yaml(:UseBlock => true)
+ if @server_facts["servername"].nil?
+ host = Facter.value(:hostname)
+ if domain = Facter.value(:domain)
+ @server_facts["servername"] = [host, domain].join(".")
else
- raise XMLRPC::FaultException.new(
- 1, "Unavailable config format %s" % format
- )
+ @server_facts["servername"] = host
end
- return CGI.escape(str)
end
end
- def local?
- if defined? @local and @local
- return true
+ # Translate our configuration appropriately for sending back to a client.
+ def translate(config)
+ if local?
+ config
else
- return false
+ CGI.escape(config.to_yaml(:UseBlock => true))
+ end
+ end
+
+ # Mark that the node has checked in. FIXME this needs to be moved into
+ # the Node class, or somewhere that's got abstract backends.
+ def update_node_check(node)
+ if Puppet.features.rails? and Puppet[:storeconfigs]
+ Puppet::Rails.connect
+
+ host = Puppet::Rails::Host.find_or_create_by_name(node.name)
+ host.last_freshcheck = Time.now
+ host.save
end
end
end
end
-
-# $Id$
diff --git a/lib/puppet/network/handler/facts.rb b/lib/puppet/network/handler/facts.rb
index e0b93f942..4767e8be4 100755
--- a/lib/puppet/network/handler/facts.rb
+++ b/lib/puppet/network/handler/facts.rb
@@ -66,5 +66,3 @@ class Puppet::Network::Handler
end
end
end
-
-# $Id$
diff --git a/lib/puppet/network/handler/fileserver.rb b/lib/puppet/network/handler/fileserver.rb
index fdf515d6e..a429412d2 100755
--- a/lib/puppet/network/handler/fileserver.rb
+++ b/lib/puppet/network/handler/fileserver.rb
@@ -239,6 +239,33 @@ class Puppet::Network::Handler
return children
end
+ # Return the mount for the Puppet modules; allows file copying from
+ # the modules.
+ def modules_mount(module_name, client)
+ # Find our environment, if we have one.
+ if node = node_handler.details(client || Facter.value("hostname"))
+ env = node.environment
+ else
+ env = nil
+ end
+
+ # And use the environment to look up the module.
+ mod = Puppet::Module::find(module_name, env)
+ if mod
+ return @mounts[MODULES].copy(mod.name, mod.files)
+ else
+ return nil
+ end
+ end
+
+ # Create a node handler instance for looking up our nodes.
+ def node_handler
+ unless defined?(@node_handler)
+ @node_handler = Puppet::Network::Handler.handler(:node).create
+ end
+ @node_handler
+ end
+
# Read the configuration file.
def readconfig(check = true)
return if @noreadconfig
@@ -388,15 +415,12 @@ class Puppet::Network::Handler
mount = nil
path = nil
if dir =~ %r{/([-\w]+)/?}
- tmp = $1
- path = dir.sub(%r{/#{tmp}/?}, '')
+ # Strip off the mount name.
+ mount_name, path = dir.sub(%r{^/}, '').split(File::Separator, 2)
- mod = Puppet::Module::find(tmp)
- if mod
- mount = @mounts[MODULES].copy(mod.name, mod.files)
- else
- unless mount = @mounts[tmp]
- raise FileServerError, "Fileserver module '%s' not mounted" % tmp
+ unless mount = modules_mount(mount_name, client)
+ unless mount = @mounts[mount_name]
+ raise FileServerError, "Fileserver module '%s' not mounted" % mount_name
end
end
else
@@ -405,7 +429,7 @@ class Puppet::Network::Handler
if path == ""
path = nil
- else
+ elsif path
# Remove any double slashes that might have occurred
path = URI.unescape(path.gsub(/\/\//, "/"))
end
diff --git a/lib/puppet/network/handler/master.rb b/lib/puppet/network/handler/master.rb
index e889c1ba8..c8db277ba 100644
--- a/lib/puppet/network/handler/master.rb
+++ b/lib/puppet/network/handler/master.rb
@@ -13,7 +13,7 @@ class Puppet::Network::Handler
include Puppet::Util
- attr_accessor :ast, :local
+ attr_accessor :ast
attr_reader :ca
@interface = XMLRPC::Service::Interface.new("puppetmaster") { |iface|
@@ -21,66 +21,10 @@ class Puppet::Network::Handler
iface.add_method("int freshness()")
}
- # FIXME At some point, this should be autodocumenting.
- def addfacts(facts)
- # Add our server version to the fact list
- facts["serverversion"] = Puppet.version.to_s
-
- # And then add the server name and IP
- {"servername" => "fqdn",
- "serverip" => "ipaddress"
- }.each do |var, fact|
- if obj = Facter[fact]
- facts[var] = obj.value
- else
- Puppet.warning "Could not retrieve fact %s" % fact
- end
- end
-
- if facts["servername"].nil?
- host = Facter.value(:hostname)
- if domain = Facter.value(:domain)
- facts["servername"] = [host, domain].join(".")
- else
- facts["servername"] = host
- end
- end
- end
-
- # Manipulate the client name as appropriate.
- def clientname(name, ip, facts)
- # Always use the hostname from Facter.
- client = facts["hostname"]
- clientip = facts["ipaddress"]
- if Puppet[:node_name] == 'cert'
- if name
- client = name
- end
- if ip
- clientip = ip
- end
- end
-
- return client, clientip
- end
-
# Tell a client whether there's a fresh config for it
def freshness(client = nil, clientip = nil)
- if Puppet.features.rails? and Puppet[:storeconfigs]
- Puppet::Rails.connect
-
- host = Puppet::Rails::Host.find_or_create_by_name(client)
- host.last_freshcheck = Time.now
- if clientip and (! host.ip or host.ip == "" or host.ip == "NULL")
- host.ip = clientip
- end
- host.save
- end
- if defined? @interpreter
- return @interpreter.parsedate
- else
- return 0
- end
+ client ||= Facter.value("hostname")
+ config_handler.version(client, clientip)
end
def initialize(hash = {})
@@ -89,8 +33,8 @@ class Puppet::Network::Handler
# Allow specification of a code snippet or of a file
if code = hash[:Code]
args[:Code] = code
- else
- args[:Manifest] = hash[:Manifest] || Puppet[:manifest]
+ elsif man = hash[:Manifest]
+ args[:Manifest] = man
end
if hash[:Local]
@@ -99,7 +43,7 @@ class Puppet::Network::Handler
@local = false
end
- args[:Local] = @local
+ args[:Local] = true
if hash.include?(:CA) and hash[:CA]
@ca = Puppet::SSLCertificates::CA.new()
@@ -121,106 +65,89 @@ class Puppet::Network::Handler
args[:Classes] = hash[:Classes]
end
- @interpreter = Puppet::Parser::Interpreter.new(args)
+ @config_handler = Puppet::Network::Handler.handler(:configuration).new(args)
end
+ # Call our various handlers; this handler is getting deprecated.
def getconfig(facts, format = "marshal", client = nil, clientip = nil)
- if @local
- # we don't need to do anything, since we should already
- # have raw objects
- Puppet.debug "Our client is local"
- else
- Puppet.debug "Our client is remote"
+ facts = decode_facts(facts)
+ client, clientip = clientname(client, clientip, facts)
- # XXX this should definitely be done in the protocol, somehow
- case format
- when "marshal":
- Puppet.warning "You should upgrade your client. 'Marshal' will not be supported much longer."
- begin
- facts = Marshal::load(CGI.unescape(facts))
- rescue => detail
- raise XMLRPC::FaultException.new(
- 1, "Could not rebuild facts"
- )
- end
- when "yaml":
- begin
- facts = YAML.load(CGI.unescape(facts))
- rescue => detail
- raise XMLRPC::FaultException.new(
- 1, "Could not rebuild facts"
- )
- end
- else
- raise XMLRPC::FaultException.new(
- 1, "Unavailable config format %s" % format
- )
- end
- end
+ # Pass the facts to the fact handler
+ fact_handler.set(client, facts)
- client, clientip = clientname(client, clientip, facts)
+ # And get the configuration from the config handler
+ begin
+ config = config_handler.configuration(client)
+ rescue => detail
+ puts detail.backtrace
+ raise
+ end
- # Add any server-side facts to our server.
- addfacts(facts)
+ return translate(config.extract)
+ end
- retobjects = nil
+ private
- # This is hackish, but there's no "silence" option for benchmarks
- # right now
- if @local
- #begin
- retobjects = @interpreter.run(client, facts)
- #rescue Puppet::Error => detail
- # Puppet.err detail
- # raise XMLRPC::FaultException.new(
- # 1, detail.to_s
- # )
- #rescue => detail
- # Puppet.err detail.to_s
- # return ""
- #end
- else
- benchmark(:notice, "Compiled configuration for %s" % client) do
- begin
- retobjects = @interpreter.run(client, facts)
- rescue Puppet::Error => detail
- Puppet.err detail
- raise XMLRPC::FaultException.new(
- 1, detail.to_s
- )
- rescue => detail
- Puppet.err detail.to_s
- return ""
- end
+ # Manipulate the client name as appropriate.
+ def clientname(name, ip, facts)
+ # Always use the hostname from Facter.
+ client = facts["hostname"]
+ clientip = facts["ipaddress"]
+ if Puppet[:node_name] == 'cert'
+ if name
+ client = name
+ end
+ if ip
+ clientip = ip
end
end
+ return client, clientip
+ end
+
+ def config_handler
+ unless defined? @config_handler
+ @config_handler = Puppet::Network::Handler.handler(:config).new :local => local?
+ end
+ @config_handler
+ end
+
+ #
+ def decode_facts(facts)
if @local
- return retobjects
+ # we don't need to do anything, since we should already
+ # have raw objects
+ Puppet.debug "Our client is local"
else
- str = nil
- case format
- when "marshal":
- str = Marshal::dump(retobjects)
- when "yaml":
- str = retobjects.to_yaml(:UseBlock => true)
- else
+ Puppet.debug "Our client is remote"
+
+ begin
+ facts = YAML.load(CGI.unescape(facts))
+ rescue => detail
raise XMLRPC::FaultException.new(
- 1, "Unavailable config format %s" % format
+ 1, "Could not rebuild facts"
)
end
- return CGI.escape(str)
end
+
+ return facts
end
- def local?
- if defined? @local and @local
- return true
+ def fact_handler
+ unless defined? @fact_handler
+ @fact_handler = Puppet::Network::Handler.handler(:facts).new :local => local?
+ end
+ @fact_handler
+ end
+
+ # Translate our configuration appropriately for sending back to a client.
+ def translate(config)
+ if local?
+ config
else
- return false
+ CGI.escape(config.to_yaml(:UseBlock => true))
end
end
end
end
-
-# $Id$
diff --git a/lib/puppet/network/handler/node.rb b/lib/puppet/network/handler/node.rb
index 898db7c22..bf2424b18 100644
--- a/lib/puppet/network/handler/node.rb
+++ b/lib/puppet/network/handler/node.rb
@@ -2,68 +2,22 @@
# Copyright (c) 2007. All rights reserved.
require 'puppet/util'
+require 'puppet/node'
require 'puppet/util/classgen'
require 'puppet/util/instance_loader'
# Look up a node, along with all the details about it.
class Puppet::Network::Handler::Node < Puppet::Network::Handler
- # A simplistic class for managing the node information itself.
- class SimpleNode
- attr_accessor :name, :classes, :parameters, :environment, :source, :ipaddress
-
- def initialize(name, options = {})
- @name = name
-
- if classes = options[:classes]
- if classes.is_a?(String)
- @classes = [classes]
- else
- @classes = classes
- end
- else
- @classes = []
- end
-
- @parameters = options[:parameters] || {}
-
- unless @environment = options[:environment]
- if env = Puppet[:environment] and env != ""
- @environment = env
- end
- end
- end
-
- # Merge the node facts with parameters from the node source.
- # This is only called if the node source has 'fact_merge' set to true.
- def fact_merge(facts)
- facts.each do |name, value|
- @parameters[name] = value unless @parameters.include?(name)
- end
- end
- end
-
desc "Retrieve information about nodes."
- extend Puppet::Util::ClassGen
- extend Puppet::Util::InstanceLoader
-
- # A simple base module we can use for modifying how our node sources work.
- module SourceBase
- include Puppet::Util::Docs
+ # Create a singleton node handler
+ def self.create
+ unless @handler
+ @handler = new
+ end
+ @handler
end
- @interface = XMLRPC::Service::Interface.new("nodes") { |iface|
- iface.add_method("string details(key)")
- iface.add_method("string parameters(key)")
- iface.add_method("string environment(key)")
- iface.add_method("string classes(key)")
- }
-
- # Set up autoloading and retrieving of reports.
- autoload :node_source, 'puppet/node_source'
-
- attr_reader :source
-
# Add a new node source.
def self.newnode_source(name, options = {}, &block)
name = symbolize(name)
@@ -103,6 +57,26 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler
rmclass(name, :hash => instance_hash(:node_source))
end
+ extend Puppet::Util::ClassGen
+ extend Puppet::Util::InstanceLoader
+
+ # A simple base module we can use for modifying how our node sources work.
+ module SourceBase
+ include Puppet::Util::Docs
+ end
+
+ @interface = XMLRPC::Service::Interface.new("nodes") { |iface|
+ iface.add_method("string details(key)")
+ iface.add_method("string parameters(key)")
+ iface.add_method("string environment(key)")
+ iface.add_method("string classes(key)")
+ }
+
+ # Set up autoloading and retrieving of reports.
+ autoload :node_source, 'puppet/node_source'
+
+ attr_reader :source
+
# Return a given node's classes.
def classes(key)
if node = details(key)
@@ -115,6 +89,10 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler
# Return an entire node configuration. This uses the 'nodesearch' method
# defined in the node_source to look for the node.
def details(key, client = nil, clientip = nil)
+ return nil unless key
+ if node = cached?(key)
+ return node
+ end
facts = node_facts(key)
node = nil
names = node_names(key, facts)
@@ -136,11 +114,15 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler
if node
node.source = @source
+ node.names = names
# Merge the facts into the parameters.
if fact_merge?
node.fact_merge(facts)
end
+
+ cache(node)
+
return node
else
return nil
@@ -167,6 +149,9 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler
extend(mod)
super
+
+ # We cache node info for speed
+ @node_cache = {}
end
# Try to retrieve a given node's parameters.
@@ -180,6 +165,23 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler
private
+ # Store the node to make things a bit faster.
+ def cache(node)
+ @node_cache[node.name] = node
+ end
+
+ # If the node is cached, return it.
+ def cached?(name)
+ # Don't use cache when the filetimeout is set to 0
+ return false if [0, "0"].include?(Puppet[:filetimeout])
+
+ if node = @node_cache[name] and Time.now - node.time < Puppet[:filetimeout]
+ return node
+ else
+ return false
+ end
+ end
+
# Create/cache a fact handler.
def fact_handler
unless defined?(@fact_handler)
@@ -191,7 +193,7 @@ class Puppet::Network::Handler::Node < Puppet::Network::Handler
# Short-hand for creating a new node, so the node sources don't need to
# specify the constant.
def newnode(options)
- SimpleNode.new(options)
+ Puppet::Node.new(options)
end
# Look up the node facts from our fact handler.
diff --git a/lib/puppet/network/handler/resource.rb b/lib/puppet/network/handler/resource.rb
index ca492bd81..ac29dce53 100755
--- a/lib/puppet/network/handler/resource.rb
+++ b/lib/puppet/network/handler/resource.rb
@@ -26,7 +26,7 @@ class Puppet::Network::Handler
# Apply a TransBucket as a transaction.
def apply(bucket, format = "yaml", client = nil, clientip = nil)
- unless @local
+ unless local?
begin
case format
when "yaml":
@@ -43,7 +43,7 @@ class Puppet::Network::Handler
# Create a client, but specify the remote machine as the server
# because the class requires it, even though it's unused
- client = Puppet::Network::Client.client(:Master).new(:Server => client||"localhost")
+ client = Puppet::Network::Client.client(:Master).new(:Master => client||"localhost")
# Set the objects
client.objects = component
diff --git a/lib/puppet/node.rb b/lib/puppet/node.rb
new file mode 100644
index 000000000..2d3ac712e
--- /dev/null
+++ b/lib/puppet/node.rb
@@ -0,0 +1,61 @@
+# A simplistic class for managing the node information itself.
+class Puppet::Node
+ attr_accessor :name, :classes, :parameters, :source, :ipaddress, :names
+ attr_reader :time
+ attr_writer :environment
+
+ # Do not return environments tha are empty string, and use
+ # explicitly set environments, then facts, then a central env
+ # value.
+ def environment
+ unless @environment and @environment != ""
+ if env = parameters["environment"] and env != ""
+ @environment = env
+ elsif env = Puppet[:environment] and env != ""
+ @environment = env
+ else
+ @environment = nil
+ end
+ end
+ @environment
+ end
+
+ def initialize(name, options = {})
+ @name = name
+
+ # Provide a default value.
+ if names = options[:names]
+ if names.is_a?(String)
+ @names = [names]
+ else
+ @names = names
+ end
+ else
+ @names = [name]
+ end
+
+ if classes = options[:classes]
+ if classes.is_a?(String)
+ @classes = [classes]
+ else
+ @classes = classes
+ end
+ else
+ @classes = []
+ end
+
+ @parameters = options[:parameters] || {}
+
+ @environment = options[:environment]
+
+ @time = Time.now
+ end
+
+ # Merge the node facts with parameters from the node source.
+ # This is only called if the node source has 'fact_merge' set to true.
+ def fact_merge(facts)
+ facts.each do |name, value|
+ @parameters[name] = value unless @parameters.include?(name)
+ end
+ end
+end
diff --git a/lib/puppet/node/configuration.rb b/lib/puppet/node/configuration.rb
new file mode 100644
index 000000000..4f93fdbe5
--- /dev/null
+++ b/lib/puppet/node/configuration.rb
@@ -0,0 +1,123 @@
+require 'puppet/external/gratr/digraph'
+
+# This class models a node configuration. It is the thing
+# meant to be passed from server to client, and it contains all
+# of the information in the configuration, including the resources
+# and the relationships between them.
+class Puppet::Node::Configuration < GRATR::Digraph
+ attr_accessor :name, :version
+ attr_reader :extraction_format
+
+ # Add classes to our class list.
+ def add_class(*classes)
+ classes.each do |klass|
+ @classes << klass
+ end
+
+ # Add the class names as tags, too.
+ tag(*classes)
+ end
+
+ def classes
+ @classes.dup
+ end
+
+ # Make sure we support the requested extraction format.
+ def extraction_format=(value)
+ unless respond_to?("extract_to_%s" % value)
+ raise ArgumentError, "Invalid extraction format %s" % value
+ end
+ @extraction_format = value
+ end
+
+ # Turn our configuration graph into whatever the client is expecting.
+ def extract
+ send("extract_to_%s" % extraction_format)
+ end
+
+ # Create the traditional TransBuckets and TransObjects from our configuration
+ # graph. This will hopefully be deprecated soon.
+ def extract_to_transportable
+ top = nil
+ current = nil
+ buckets = {}
+
+ unless main = vertices.find { |res| res.type == "class" and res.title == :main }
+ raise Puppet::DevError, "Could not find 'main' class; cannot generate configuration"
+ end
+
+ # Create a proc for examining edges, which we'll use to build our tree
+ # of TransBuckets and TransObjects.
+ bucket = nil
+ edges = proc do |edge|
+ # The sources are always non-builtins.
+ source, target = edge.source, edge.target
+ unless tmp = buckets[source.to_s]
+ if tmp = buckets[source.to_s] = source.to_trans
+ bucket = tmp
+ else
+ # This is because virtual resources return nil. If a virtual
+ # container resource contains realized resources, we still need to get
+ # to them. So, we keep a reference to the last valid bucket
+ # we returned and use that if the container resource is virtual.
+ end
+ end
+ bucket = tmp || bucket
+ if child = target.to_trans
+ unless bucket
+ raise "No bucket created for %s" % source
+ end
+ bucket.push child
+
+ # It's important that we keep a reference to any TransBuckets we've created, so
+ # we don't create multiple buckets for children.
+ unless target.builtin?
+ buckets[target.to_s] = child
+ end
+ end
+ end
+ dfs(:start => main, :examine_edge => edges)
+
+ unless main
+ raise Puppet::DevError, "Could not find 'main' class; cannot generate configuration"
+ end
+
+ # Retrive the bucket for the top-level scope and set the appropriate metadata.
+ unless result = buckets[main.to_s]
+ raise Puppet::DevError, "Did not evaluate top scope"
+ end
+
+ result.classes = classes
+
+ # Clear the cache to encourage the GC
+ buckets.clear
+ return result
+ end
+
+ def initialize(name)
+ super()
+ @name = name
+ @extraction_format ||= :transportable
+ @tags = []
+ @classes = []
+ end
+
+ # Add a tag.
+ def tag(*names)
+ names.each do |name|
+ name = name.to_s
+ @tags << name unless @tags.include?(name)
+ if name.include?("::")
+ name.split("::").each do |sub|
+ @tags << sub unless @tags.include?(sub)
+ end
+ end
+ end
+ nil
+ end
+
+ # Return the list of tags.
+ def tags
+ @tags.dup
+ end
+end
diff --git a/lib/puppet/node_source/ldap.rb b/lib/puppet/node_source/ldap.rb
index 9332fcb40..7b60a3c62 100644
--- a/lib/puppet/node_source/ldap.rb
+++ b/lib/puppet/node_source/ldap.rb
@@ -4,14 +4,6 @@ Puppet::Network::Handler::Node.newnode_source(:ldap, :fact_merge => true) do
# Find the ldap node, return the class list and parent node specially,
# and everything else in a parameter hash.
def ldapsearch(node)
- unless defined? @ldap and @ldap
- setup_ldap()
- unless @ldap
- Puppet.info "Skipping ldap source; no ldap connection"
- return nil
- end
- end
-
filter = Puppet[:ldapstring]
classattrs = Puppet[:ldapclassattrs].split("\s*,\s*")
if Puppet[:ldapattrs] == "all"
@@ -42,7 +34,7 @@ Puppet::Network::Handler::Node.newnode_source(:ldap, :fact_merge => true) do
begin
# We're always doing a sub here; oh well.
- @ldap.search(Puppet[:ldapbase], 2, filter, search_attrs) do |entry|
+ ldap.search(Puppet[:ldapbase], 2, filter, search_attrs) do |entry|
found = true
if pattr
if values = entry.vals(pattr)
@@ -76,7 +68,6 @@ Puppet::Network::Handler::Node.newnode_source(:ldap, :fact_merge => true) do
if count == 0
# Try reconnecting to ldap
@ldap = nil
- setup_ldap()
retry
else
raise Puppet::Error, "LDAP Search failed: %s" % detail
@@ -115,4 +106,33 @@ Puppet::Network::Handler::Node.newnode_source(:ldap, :fact_merge => true) do
return newnode(node, :classes => classes, :source => "ldap", :parameters => parameters)
end
+
+ private
+
+ # Create an ldap connection.
+ def ldap
+ unless defined? @ldap and @ldap
+ unless Puppet.features.ldap?
+ raise Puppet::Error, "Could not set up LDAP Connection: Missing ruby/ldap libraries"
+ end
+ begin
+ if Puppet[:ldapssl]
+ @ldap = LDAP::SSLConn.new(Puppet[:ldapserver], Puppet[:ldapport])
+ elsif Puppet[:ldaptls]
+ @ldap = LDAP::SSLConn.new(
+ Puppet[:ldapserver], Puppet[:ldapport], true
+ )
+ else
+ @ldap = LDAP::Conn.new(Puppet[:ldapserver], Puppet[:ldapport])
+ end
+ @ldap.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
+ @ldap.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON)
+ @ldap.simple_bind(Puppet[:ldapuser], Puppet[:ldappassword])
+ rescue => detail
+ raise Puppet::Error, "Could not connect to LDAP: %s" % detail
+ end
+ end
+
+ return @ldap
+ end
end
diff --git a/lib/puppet/parser/ast/astarray.rb b/lib/puppet/parser/ast/astarray.rb
index a0bd5bf89..9a2dc286c 100644
--- a/lib/puppet/parser/ast/astarray.rb
+++ b/lib/puppet/parser/ast/astarray.rb
@@ -21,45 +21,38 @@ class Puppet::Parser::AST
# We basically always operate declaratively, and when we
# do we need to evaluate the settor-like statements first. This
# is basically variable and type-default declarations.
- if scope.declarative?
- # This is such a stupid hack. I've no real idea how to make a
- # "real" declarative language, so I hack it so it looks like
- # one, yay.
- settors = []
- others = []
+ # This is such a stupid hack. I've no real idea how to make a
+ # "real" declarative language, so I hack it so it looks like
+ # one, yay.
+ settors = []
+ others = []
- # Make a new array, so we don't have to deal with the details of
- # flattening and such
- items = []
-
- # First clean out any AST::ASTArrays
- @children.each { |child|
- if child.instance_of?(AST::ASTArray)
- child.each do |ac|
- if ac.class.settor?
- settors << ac
- else
- others << ac
- end
- end
- else
- if child.class.settor?
- settors << child
+ # Make a new array, so we don't have to deal with the details of
+ # flattening and such
+ items = []
+
+ # First clean out any AST::ASTArrays
+ @children.each { |child|
+ if child.instance_of?(AST::ASTArray)
+ child.each do |ac|
+ if ac.class.settor?
+ settors << ac
else
- others << child
+ others << ac
end
end
- }
- rets = [settors, others].flatten.collect { |child|
- child.safeevaluate(:scope => scope)
- }
- return rets.reject { |o| o.nil? }
- else
- # If we're not declarative, just do everything in order.
- return @children.collect { |item|
- item.safeevaluate(:scope => scope)
- }.reject { |o| o.nil? }
- end
+ else
+ if child.class.settor?
+ settors << child
+ else
+ others << child
+ end
+ end
+ }
+ rets = [settors, others].flatten.collect { |child|
+ child.safeevaluate(:scope => scope)
+ }
+ return rets.reject { |o| o.nil? }
end
def push(*ary)
@@ -72,28 +65,11 @@ class Puppet::Parser::AST
return self
end
-
- # Convert to a string. Only used for printing the parse tree.
- def to_s
- return "[" + @children.collect { |child|
- child.to_s
- }.join(", ") + "]"
- end
-
- # Print the parse tree.
- def tree(indent = 0)
- #puts((AST.indent * indent) + self.pin)
- self.collect { |child|
- child.tree(indent)
- }.join("\n" + (AST.midline * (indent+1)) + "\n")
- end
end
# A simple container class, containing the parameters for an object.
# Used for abstracting the grammar declarations. Basically unnecessary
# except that I kept finding bugs because I had too many arrays that
# meant completely different things.
- class ResourceInst < ASTArray; end
+ class ResourceInstance < ASTArray; end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/branch.rb b/lib/puppet/parser/ast/branch.rb
index 5a4d323f5..dcd09baa3 100644
--- a/lib/puppet/parser/ast/branch.rb
+++ b/lib/puppet/parser/ast/branch.rb
@@ -35,15 +35,5 @@ class Puppet::Parser::AST
end
}
end
-
- # Pretty-print the parse tree.
- def tree(indent = 0)
- return ((@@indline * indent) +
- self.typewrap(self.pin)) + "\n" + self.collect { |child|
- child.tree(indent + 1)
- }.join("\n")
- end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/caseopt.rb b/lib/puppet/parser/ast/caseopt.rb
index 11483d082..d1d9d0e9c 100644
--- a/lib/puppet/parser/ast/caseopt.rb
+++ b/lib/puppet/parser/ast/caseopt.rb
@@ -56,16 +56,5 @@ class Puppet::Parser::AST
def evaluate(hash)
return @statements.safeevaluate(hash)
end
-
- def tree(indent = 0)
- rettree = [
- @value.tree(indent + 1),
- ((@@indline * indent) + self.typewrap(self.pin)),
- @statements.tree(indent + 1)
- ]
- return rettree.flatten.join("\n")
- end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/casestatement.rb b/lib/puppet/parser/ast/casestatement.rb
index c56e33bc9..3c6f9c7e2 100644
--- a/lib/puppet/parser/ast/casestatement.rb
+++ b/lib/puppet/parser/ast/casestatement.rb
@@ -51,20 +51,8 @@ class Puppet::Parser::AST
return retvalue
end
- def tree(indent = 0)
- rettree = [
- @test.tree(indent + 1),
- ((@@indline * indent) + self.typewrap(self.pin)),
- @options.tree(indent + 1)
- ]
-
- return rettree.flatten.join("\n")
- end
-
def each
[@test,@options].each { |child| yield child }
end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/collection.rb b/lib/puppet/parser/ast/collection.rb
index c817b5c5e..e05977a47 100644
--- a/lib/puppet/parser/ast/collection.rb
+++ b/lib/puppet/parser/ast/collection.rb
@@ -20,11 +20,9 @@ class Collection < AST::Branch
newcoll = Puppet::Parser::Collector.new(scope, @type, str, code, self.form)
- scope.newcollection(newcoll)
+ scope.compile.add_collection(newcoll)
newcoll
end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/collexpr.rb b/lib/puppet/parser/ast/collexpr.rb
index 5fb11c709..4a96d9c61 100644
--- a/lib/puppet/parser/ast/collexpr.rb
+++ b/lib/puppet/parser/ast/collexpr.rb
@@ -77,5 +77,3 @@ class CollExpr < AST::Branch
end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/component.rb b/lib/puppet/parser/ast/component.rb
index 65f310212..1d7fe9cdd 100644
--- a/lib/puppet/parser/ast/component.rb
+++ b/lib/puppet/parser/ast/component.rb
@@ -27,7 +27,7 @@ class Puppet::Parser::AST
false
end
- def evaluate(hash)
+ def evaluate_resource(hash)
origscope = hash[:scope]
title = hash[:title]
args = symbolize_options(hash[:arguments] || {})
@@ -222,5 +222,3 @@ class Puppet::Parser::AST
end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/definition.rb b/lib/puppet/parser/ast/definition.rb
new file mode 100644
index 000000000..1cdaa6431
--- /dev/null
+++ b/lib/puppet/parser/ast/definition.rb
@@ -0,0 +1,214 @@
+require 'puppet/parser/ast/branch'
+
+class Puppet::Parser::AST
+ # Evaluate the stored parse tree for a given component. This will
+ # receive the arguments passed to the component and also the type and
+ # name of the component.
+ class Definition < AST::Branch
+ include Puppet::Util
+ include Puppet::Util::Warnings
+ include Puppet::Util::MethodHelper
+ class << self
+ attr_accessor :name
+ end
+
+ # The class name
+ @name = :definition
+
+ attr_accessor :classname, :arguments, :code, :scope, :keyword
+ attr_accessor :exported, :namespace, :parser, :virtual
+
+ # These are retrieved when looking up the superclass
+ attr_accessor :name
+
+ attr_reader :parentclass
+
+ def child_of?(klass)
+ false
+ end
+
+ def evaluate(options)
+ origscope = options[:scope]
+ resource = options[:resource]
+
+ # Create a new scope.
+ scope = subscope(origscope, resource)
+
+ # Additionally, add a tag for whatever kind of class
+ # we are
+ if @classname != "" and ! @classname.nil?
+ @classname.split(/::/).each { |tag| scope.resource.tag(tag) }
+ end
+
+ [resource.name, resource.title].each do |str|
+ unless str.nil? or str =~ /[^\w]/ or str == ""
+ scope.resource.tag(str)
+ end
+ end
+
+ set_resource_parameters(scope, resource)
+
+ if self.code
+ return self.code.safeevaluate(:scope => scope)
+ else
+ return nil
+ end
+ end
+
+ def initialize(hash = {})
+ @arguments = nil
+ @parentclass = nil
+ super
+
+ # Convert the arguments to a hash for ease of later use.
+ if @arguments
+ unless @arguments.is_a? Array
+ @arguments = [@arguments]
+ end
+ oldargs = @arguments
+ @arguments = {}
+ oldargs.each do |arg, val|
+ @arguments[arg] = val
+ end
+ else
+ @arguments = {}
+ end
+
+ # Deal with metaparams in the argument list.
+ @arguments.each do |arg, defvalue|
+ next unless Puppet::Type.metaparamclass(arg)
+ if defvalue
+ warnonce "%s is a metaparam; this value will inherit to all contained resources" % arg
+ else
+ raise Puppet::ParseError,
+ "%s is a metaparameter; please choose another name" %
+ name
+ end
+ end
+ end
+
+ def find_parentclass
+ @parser.findclass(namespace, parentclass)
+ end
+
+ # Set our parent class, with a little check to avoid some potential
+ # weirdness.
+ def parentclass=(name)
+ if name == self.classname
+ parsefail "Parent classes must have dissimilar names"
+ end
+
+ @parentclass = name
+ end
+
+ # Hunt down our class object.
+ def parentobj
+ if @parentclass
+ # Cache our result, since it should never change.
+ unless defined?(@parentobj)
+ unless tmp = find_parentclass
+ parsefail "Could not find %s %s" % [self.class.name, @parentclass]
+ end
+
+ if tmp == self
+ parsefail "Parent classes must have dissimilar names"
+ end
+
+ @parentobj = tmp
+ end
+ @parentobj
+ else
+ nil
+ end
+ end
+
+ # Create a new subscope in which to evaluate our code.
+ def subscope(scope, resource)
+ args = {
+ :resource => resource,
+ :keyword => self.keyword,
+ :namespace => self.namespace,
+ :source => self
+ }
+
+ oldscope = scope
+ scope = scope.newscope(args)
+ scope.source = self
+
+ return scope
+ end
+
+ def to_s
+ classname
+ end
+
+ # Check whether a given argument is valid. Searches up through
+ # any parent classes that might exist.
+ def validattr?(param)
+ param = param.to_s
+
+ if @arguments.include?(param)
+ # It's a valid arg for us
+ return true
+ elsif param == "name"
+ return true
+# elsif defined? @parentclass and @parentclass
+# # Else, check any existing parent
+# if parent = @scope.lookuptype(@parentclass) and parent != []
+# return parent.validarg?(param)
+# elsif builtin = Puppet::Type.type(@parentclass)
+# return builtin.validattr?(param)
+# else
+# raise Puppet::Error, "Could not find parent class %s" %
+# @parentclass
+# end
+ elsif Puppet::Type.metaparam?(param)
+ return true
+ else
+ # Or just return false
+ return false
+ end
+ end
+
+ private
+
+ # Set any arguments passed by the resource as variables in the scope.
+ def set_resource_parameters(scope, resource)
+ args = symbolize_options(resource.to_hash || {})
+
+ # Verify that all required arguments are either present or
+ # have been provided with defaults.
+ if self.arguments
+ self.arguments.each { |arg, default|
+ arg = symbolize(arg)
+ unless args.include?(arg)
+ if defined? default and ! default.nil?
+ default = default.safeevaluate :scope => scope
+ args[arg] = default
+ #Puppet.debug "Got default %s for %s in %s" %
+ # [default.inspect, arg.inspect, @name.inspect]
+ else
+ parsefail "Must pass %s to %s of type %s" %
+ [arg, resource.title, @classname]
+ end
+ end
+ }
+ end
+
+ # Set each of the provided arguments as variables in the
+ # definition's scope.
+ args.each { |arg,value|
+ unless validattr?(arg)
+ parsefail "%s does not accept attribute %s" % [@classname, arg]
+ end
+
+ exceptwrap do
+ scope.setvar(arg.to_s, args[arg])
+ end
+ }
+
+ scope.setvar("title", resource.title) unless args.include? :title
+ scope.setvar("name", resource.name) unless args.include? :name
+ end
+ end
+end
diff --git a/lib/puppet/parser/ast/else.rb b/lib/puppet/parser/ast/else.rb
index ff2f233b7..e76051372 100644
--- a/lib/puppet/parser/ast/else.rb
+++ b/lib/puppet/parser/ast/else.rb
@@ -16,15 +16,5 @@ class Puppet::Parser::AST
scope = hash[:scope]
return @statements.safeevaluate(:scope => scope)
end
-
- def tree(indent = 0)
- rettree = [
- ((@@indline * indent) + self.typewrap(self.pin)),
- @statements.tree(indent + 1)
- ]
- return rettree.flatten.join("\n")
- end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/function.rb b/lib/puppet/parser/ast/function.rb
index 052b8a8b1..0cd1fff62 100644
--- a/lib/puppet/parser/ast/function.rb
+++ b/lib/puppet/parser/ast/function.rb
@@ -53,5 +53,3 @@ class Puppet::Parser::AST
end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/hostclass.rb b/lib/puppet/parser/ast/hostclass.rb
index d1ce370da..9c039bb49 100644
--- a/lib/puppet/parser/ast/hostclass.rb
+++ b/lib/puppet/parser/ast/hostclass.rb
@@ -1,10 +1,10 @@
-require 'puppet/parser/ast/component'
+require 'puppet/parser/ast/definition'
class Puppet::Parser::AST
- # The code associated with a class. This is different from components
+ # The code associated with a class. This is different from definitions
# in that each class is a singleton -- only one will exist for a given
# node.
- class HostClass < AST::Component
+ class HostClass < AST::Definition
@name = :class
# Are we a child of the passed class? Do a recursive search up our
@@ -20,29 +20,30 @@ class Puppet::Parser::AST
end
# Evaluate the code associated with this class.
- def evaluate(hash)
- scope = hash[:scope]
- args = hash[:arguments]
-
- # Verify that we haven't already been evaluated, and if we have been evaluated,
- # make sure that we match the class.
- if existing_scope = scope.class_scope(self)
- raise "Fix this portion of the code -- check that the scopes match classes"
- #if existing_scope.source.object_id == self.object_id
+ def evaluate(options)
+ scope = options[:scope]
+ raise(ArgumentError, "Classes require resources") unless options[:resource]
+ # Verify that we haven't already been evaluated. This is
+ # what provides the singleton aspect.
+ if existing_scope = scope.compile.class_scope(self)
Puppet.debug "%s class already evaluated" % @type
return nil
end
+ scope.compile.configuration.tag(self.classname)
+
pnames = nil
if pklass = self.parentobj
- pklass.safeevaluate :scope => scope
+ pklass.safeevaluate :scope => scope, :resource => options[:resource]
scope = parent_scope(scope, pklass)
pnames = scope.namespaces
end
- unless hash[:nosubscope]
- scope = subscope(scope)
+ # Don't create a subscope for the top-level class, since it already
+ # has its own scope.
+ unless options[:resource].title == :main
+ scope = subscope(scope, options[:resource])
end
if pnames
@@ -53,7 +54,7 @@ class Puppet::Parser::AST
# Set the class before we do anything else, so that it's set
# during the evaluation and can be inspected.
- scope.setclass(self)
+ scope.compile.class_set(self.classname, scope)
# Now evaluate our code, yo.
if self.code
@@ -63,19 +64,17 @@ class Puppet::Parser::AST
end
end
- def initialize(hash)
+ def initialize(options)
@parentclass = nil
super
end
def parent_scope(scope, klass)
- if s = scope.class_scope(klass)
+ if s = scope.compile.class_scope(klass)
return s
else
- raise Puppet::DevError, "Could not find scope for %s" % klass.fqname
+ raise Puppet::DevError, "Could not find scope for %s" % klass.classname
end
end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/ifstatement.rb b/lib/puppet/parser/ast/ifstatement.rb
index 300f68dab..66a07b01f 100644
--- a/lib/puppet/parser/ast/ifstatement.rb
+++ b/lib/puppet/parser/ast/ifstatement.rb
@@ -26,18 +26,5 @@ class Puppet::Parser::AST
end
end
end
-
- def tree(indent = 0)
- rettree = [
- @test.tree(indent + 1),
- ((@@indline * indent) + self.typewrap(self.pin)),
- @statements.tree(indent + 1),
- @else.tree(indent + 1)
- ]
-
- return rettree.flatten.join("\n")
- end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/leaf.rb b/lib/puppet/parser/ast/leaf.rb
index c2fd0939d..225253061 100644
--- a/lib/puppet/parser/ast/leaf.rb
+++ b/lib/puppet/parser/ast/leaf.rb
@@ -10,11 +10,6 @@ class Puppet::Parser::AST
return @value
end
- # Print the value in parse tree context.
- def tree(indent = 0)
- return ((@@indent * indent) + self.typewrap(self.value))
- end
-
def to_s
return @value
end
@@ -92,7 +87,4 @@ class Puppet::Parser::AST
end
end
end
-
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/node.rb b/lib/puppet/parser/ast/node.rb
index b9052168a..a296e43ba 100644
--- a/lib/puppet/parser/ast/node.rb
+++ b/lib/puppet/parser/ast/node.rb
@@ -7,11 +7,10 @@ class Puppet::Parser::AST
@name = :node
attr_accessor :name
- #def evaluate(scope, facts = {})
- def evaluate(hash)
- scope = hash[:scope]
+ def evaluate(options)
+ scope = options[:scope]
- #pscope = if ! Puppet[:lexical] or hash[:asparent]
+ #pscope = if ! Puppet[:lexical] or options[:asparent]
# @scope
#else
# origscope
@@ -20,11 +19,11 @@ class Puppet::Parser::AST
# We don't have to worry about the declarativeness of node parentage,
# because the entry point is always a single node definition.
if parent = self.parentobj
- scope = parent.safeevaluate :scope => scope
+ scope = parent.safeevaluate :scope => scope, :resource => options[:resource]
end
scope = scope.newscope(
- :type => self.name,
+ :resource => options[:resource],
:keyword => @keyword,
:source => self,
:namespace => "" # nodes are always in ""
@@ -33,7 +32,7 @@ class Puppet::Parser::AST
# Mark our node name as a class, too, but strip it of the domain
# name. Make the mark before we evaluate the code, so that it is
# marked within the code itself.
- scope.setclass(self)
+ scope.compile.class_set(self.classname, scope)
# And then evaluate our code if we have any
if self.code
@@ -43,7 +42,7 @@ class Puppet::Parser::AST
return scope
end
- def initialize(hash)
+ def initialize(options)
@parentclass = nil
super
@@ -53,6 +52,12 @@ class Puppet::Parser::AST
end
end
+ # Make sure node scopes are marked as such.
+ def subscope(*args)
+ scope = super
+ scope.nodescope = true
+ end
+
private
# Search for the object matching our parent class.
def find_parentclass
@@ -60,5 +65,3 @@ class Puppet::Parser::AST
end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/resource.rb b/lib/puppet/parser/ast/resource.rb
new file mode 100644
index 000000000..c53ab0a68
--- /dev/null
+++ b/lib/puppet/parser/ast/resource.rb
@@ -0,0 +1,75 @@
+require 'puppet/parser/ast/resource_reference'
+
+# Any normal puppet resource declaration. Can point to a definition or a
+# builtin type.
+class Puppet::Parser::AST
+class Resource < AST::ResourceReference
+ attr_accessor :title, :type, :exported, :virtual
+ attr_reader :params
+
+ # Does not actually return an object; instead sets an object
+ # in the current scope.
+ def evaluate(options)
+ scope = options[:scope]
+
+ # Evaluate all of the specified params.
+ paramobjects = @params.collect { |param|
+ param.safeevaluate(:scope => scope)
+ }
+
+ objtitles = @title.safeevaluate(:scope => scope)
+
+ # it's easier to always use an array, even for only one name
+ unless objtitles.is_a?(Array)
+ objtitles = [objtitles]
+ end
+
+ objtype = qualified_type(scope)
+
+ # This is where our implicit iteration takes place; if someone
+ # passed an array as the name, then we act just like the called us
+ # many times.
+ objtitles.collect { |objtitle|
+ exceptwrap :type => Puppet::ParseError do
+ exp = self.exported || scope.resource.exported?
+ # We want virtual to be true if exported is true. We can't
+ # just set :virtual => self.virtual in the initialization,
+ # because sometimes the :virtual attribute is set *after*
+ # :exported, in which case it clobbers :exported if :exported
+ # is true. Argh, this was a very tough one to track down.
+ virt = self.virtual || scope.resource.virtual? || exp
+ obj = Puppet::Parser::Resource.new(
+ :type => objtype,
+ :title => objtitle,
+ :params => paramobjects,
+ :file => self.file,
+ :line => self.line,
+ :exported => exp,
+ :virtual => virt,
+ :source => scope.source,
+ :scope => scope
+ )
+
+ # And then store the resource in the compile.
+ # At some point, we need to switch all of this to return
+ # objects instead of storing them like this.
+ scope.compile.store_resource(scope, obj)
+ obj
+ end
+ }.reject { |obj| obj.nil? }
+ end
+
+ # Set the parameters for our object.
+ def params=(params)
+ if params.is_a?(AST::ASTArray)
+ @params = params
+ else
+ @params = AST::ASTArray.new(
+ :line => params.line,
+ :file => params.file,
+ :children => [params]
+ )
+ end
+ end
+end
+end
diff --git a/lib/puppet/parser/ast/resourcedefaults.rb b/lib/puppet/parser/ast/resource_defaults.rb
index df16b1b59..44ec146b0 100644
--- a/lib/puppet/parser/ast/resourcedefaults.rb
+++ b/lib/puppet/parser/ast/resource_defaults.rb
@@ -6,10 +6,6 @@ class Puppet::Parser::AST
class ResourceDefaults < AST::Branch
attr_accessor :type, :params
- def each
- [@type,@params].each { |child| yield child }
- end
-
# As opposed to ResourceDef, this stores each default for the given
# object type.
def evaluate(hash)
@@ -21,20 +17,5 @@ class Puppet::Parser::AST
scope.setdefaults(type, params)
end
end
-
- def tree(indent = 0)
- return [
- @type.tree(indent + 1),
- ((@@indline * 4 * indent) + self.typewrap(self.pin)),
- @params.tree(indent + 1)
- ].join("\n")
- end
-
- def to_s
- return "%s { %s }" % [@type,@params]
- end
end
-
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/resourceoverride.rb b/lib/puppet/parser/ast/resource_override.rb
index 26d69ae97..46c930902 100644
--- a/lib/puppet/parser/ast/resourceoverride.rb
+++ b/lib/puppet/parser/ast/resource_override.rb
@@ -1,9 +1,9 @@
-require 'puppet/parser/ast/resourcedef'
+require 'puppet/parser/ast/resource'
class Puppet::Parser::AST
# Set a parameter on a resource specification created somewhere else in the
# configuration. The object is responsible for verifying that this is allowed.
- class ResourceOverride < ResourceDef
+ class ResourceOverride < Resource
attr_accessor :object
attr_reader :params
@@ -44,7 +44,7 @@ class Puppet::Parser::AST
# Now we tell the scope that it's an override, and it behaves as
# necessary.
- scope.setoverride(obj)
+ scope.compile.store_override(obj)
obj
end
@@ -58,5 +58,3 @@ class Puppet::Parser::AST
end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/resource_reference.rb b/lib/puppet/parser/ast/resource_reference.rb
new file mode 100644
index 000000000..b8edff8f1
--- /dev/null
+++ b/lib/puppet/parser/ast/resource_reference.rb
@@ -0,0 +1,66 @@
+require 'puppet/parser/ast/branch'
+
+class Puppet::Parser::AST
+ # A reference to an object. Only valid as an rvalue.
+ class ResourceReference < AST::Branch
+ attr_accessor :title, :type
+ # Is the type a builtin type?
+ def builtintype?(type)
+ if typeklass = Puppet::Type.type(type)
+ return typeklass
+ else
+ return false
+ end
+ end
+
+ def each
+ [@type,@title].flatten.each { |param|
+ #Puppet.debug("yielding param %s" % param)
+ yield param
+ }
+ end
+
+ # Evaluate our object, but just return a simple array of the type
+ # and name.
+ def evaluate(hash)
+ scope = hash[:scope]
+
+ title = @title.safeevaluate(:scope => scope)
+ if @type == "class"
+ objtype = @type
+ title = qualified_class(scope, title)
+ else
+ objtype = qualified_type(scope)
+ end
+
+ return Puppet::Parser::Resource::Reference.new(
+ :type => objtype, :title => title
+ )
+ end
+
+ # Look up a fully qualified class name.
+ def qualified_class(scope, title)
+ # Look up the full path to the class
+ if classobj = scope.findclass(title)
+ title = classobj.classname
+ else
+ raise Puppet::ParseError, "Could not find class %s" % title
+ end
+ end
+
+ # Look up a fully-qualified type. This method is
+ # also used in AST::Resource.
+ def qualified_type(scope, title = nil)
+ # We want a lower-case type. For some reason.
+ objtype = @type.downcase
+ unless builtintype?(objtype)
+ if dtype = scope.finddefine(objtype)
+ objtype = dtype.classname
+ else
+ raise Puppet::ParseError, "Could not find resource type %s" % objtype
+ end
+ end
+ return objtype
+ end
+ end
+end
diff --git a/lib/puppet/parser/ast/resourcedef.rb b/lib/puppet/parser/ast/resourcedef.rb
deleted file mode 100644
index dfd7c447e..000000000
--- a/lib/puppet/parser/ast/resourcedef.rb
+++ /dev/null
@@ -1,222 +0,0 @@
-require 'puppet/parser/ast/branch'
-
-# Any normal puppet object declaration. Can result in a class or a
-# component, in addition to builtin types.
-class Puppet::Parser::AST
-class ResourceDef < AST::Branch
- attr_accessor :title, :type, :exported, :virtual
- attr_reader :params
-
- # probably not used at all
- def []=(index,obj)
- @params[index] = obj
- end
-
- # probably not used at all
- def [](index)
- return @params[index]
- end
-
- # Iterate across all of our children.
- def each
- [@type,@title,@params].flatten.each { |param|
- #Puppet.debug("yielding param %s" % param)
- yield param
- }
- end
-
- # Does not actually return an object; instead sets an object
- # in the current scope.
- def evaluate(hash)
- scope = hash[:scope]
- @scope = scope
- hash = {}
-
- # Get our type and name.
- objtype = @type
-
- # Disable definition inheritance, for now. 8/27/06, luke
- #if objtype == "super"
- # objtype = supertype()
- # @subtype = true
- #else
- @subtype = false
- #end
-
- # Evaluate all of the specified params.
- paramobjects = @params.collect { |param|
- param.safeevaluate(:scope => scope)
- }
-
- # Now collect info from our parent.
- parentname = nil
- if @subtype
- parentname = supersetup(hash)
- end
-
- objtitles = nil
- # Determine our name if we have one.
- if self.title
- objtitles = @title.safeevaluate(:scope => scope)
- # it's easier to always use an array, even for only one name
- unless objtitles.is_a?(Array)
- objtitles = [objtitles]
- end
- else
- if parentname
- objtitles = [parentname]
- else
- # See if they specified the name as a parameter instead of
- # as a normal name (i.e., before the colon).
- unless object # we're a builtin
- if objclass = Puppet::Type.type(objtype)
- namevar = objclass.namevar
-
- tmp = hash["name"] || hash[namevar.to_s]
-
- if tmp
- objtitles = [tmp]
- end
- else
- # This isn't grammatically legal.
- raise Puppet::ParseError, "Got a resource with no title"
- end
- end
- end
- end
-
- # This is where our implicit iteration takes place; if someone
- # passed an array as the name, then we act just like the called us
- # many times.
- objtitles.collect { |objtitle|
- exceptwrap :type => Puppet::ParseError do
- exp = self.exported || scope.exported
- # We want virtual to be true if exported is true. We can't
- # just set :virtual => self.virtual in the initialization,
- # because sometimes the :virtual attribute is set *after*
- # :exported, in which case it clobbers :exported if :exported
- # is true. Argh, this was a very tough one to track down.
- virt = self.virtual || exported
- obj = Puppet::Parser::Resource.new(
- :type => objtype,
- :title => objtitle,
- :params => paramobjects,
- :file => @file,
- :line => @line,
- :exported => exp,
- :virtual => virt,
- :source => scope.source,
- :scope => scope
- )
-
- # And then store the resource in the scope.
- # XXX At some point, we need to switch all of this to return
- # objects instead of storing them like this.
- scope.setresource(obj)
- obj
- end
- }.reject { |obj| obj.nil? }
- end
-
- # Create our ResourceDef. Handles type checking for us.
- def initialize(hash)
- @checked = false
- super
-
- #self.typecheck(@type.value)
- end
-
- # Set the parameters for our object.
- def params=(params)
- if params.is_a?(AST::ASTArray)
- @params = params
- else
- @params = AST::ASTArray.new(
- :line => params.line,
- :file => params.file,
- :children => [params]
- )
- end
- end
-
- def supercomp
- unless defined? @supercomp
- if @scope and comp = @scope.inside
- @supercomp = comp
- else
- error = Puppet::ParseError.new(
- "'super' is only valid within definitions"
- )
- error.line = self.line
- error.file = self.file
- raise error
- end
- end
- @supercomp
- end
-
- # Take all of the arguments of our parent and add them into our own,
- # without overriding anything.
- def supersetup(hash)
- comp = supercomp()
-
- # Now check each of the arguments from the parent.
- comp.arguments.each do |name, value|
- unless hash.has_key? name
- hash[name] = value
- end
- end
-
- # Return the parent name, so it can be used if appropriate.
- return comp.name
- end
-
- # Retrieve our supertype.
- def supertype
- unless defined? @supertype
- if parent = supercomp.parentclass
- @supertype = parent
- else
- error = Puppet::ParseError.new(
- "%s does not have a parent class" % comp.type
- )
- error.line = self.line
- error.file = self.file
- raise error
- end
- end
- @supertype
- end
-
- # Print this object out.
- def tree(indent = 0)
- return [
- @type.tree(indent + 1),
- @title.tree(indent + 1),
- ((@@indline * indent) + self.typewrap(self.pin)),
- @params.collect { |param|
- begin
- param.tree(indent + 1)
- rescue NoMethodError => detail
- Puppet.err @params.inspect
- error = Puppet::DevError.new(
- "failed to tree a %s" % self.class
- )
- error.set_backtrace detail.backtrace
- raise error
- end
- }.join("\n")
- ].join("\n")
- end
-
- def to_s
- return "%s => { %s }" % [@title,
- @params.collect { |param|
- param.to_s
- }.join("\n")
- ]
- end
-end
-end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/resourceparam.rb b/lib/puppet/parser/ast/resourceparam.rb
index d87720160..8b1e7b367 100644
--- a/lib/puppet/parser/ast/resourceparam.rb
+++ b/lib/puppet/parser/ast/resourceparam.rb
@@ -20,19 +20,5 @@ class Puppet::Parser::AST
:add => self.add
)
end
-
- def tree(indent = 0)
- return [
- @param.tree(indent + 1),
- ((@@indline * indent) + self.typewrap(self.pin)),
- @value.tree(indent + 1)
- ].join("\n")
- end
-
- def to_s
- return "%s => %s" % [@param,@value]
- end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/resourceref.rb b/lib/puppet/parser/ast/resourceref.rb
deleted file mode 100644
index e5bb69a46..000000000
--- a/lib/puppet/parser/ast/resourceref.rb
+++ /dev/null
@@ -1,58 +0,0 @@
-require 'puppet/parser/ast/branch'
-
-class Puppet::Parser::AST
- # A reference to an object. Only valid as an rvalue.
- class ResourceRef < AST::Branch
- attr_accessor :title, :type
-
- def each
- [@type,@title].flatten.each { |param|
- #Puppet.debug("yielding param %s" % param)
- yield param
- }
- end
-
- # Evaluate our object, but just return a simple array of the type
- # and name.
- def evaluate(hash)
- scope = hash[:scope]
-
- # We want a lower-case type.
- objtype = @type.downcase
- title = @title.safeevaluate(:scope => scope)
-
- if scope.builtintype?(objtype)
- # nothing
- elsif dtype = scope.finddefine(objtype)
- objtype = dtype.classname
- elsif objtype == "class"
- # Look up the full path to the class
- if classobj = scope.findclass(title)
- title = classobj.classname
- else
- raise Puppet::ParseError, "Could not find class %s" % title
- end
- else
- raise Puppet::ParseError, "Could not find resource type %s" % objtype
- end
-
- return Puppet::Parser::Resource::Reference.new(
- :type => objtype, :title => title
- )
- end
-
- def tree(indent = 0)
- return [
- @type.tree(indent + 1),
- @title.tree(indent + 1),
- ((@@indline * indent) + self.typewrap(self.pin))
- ].join("\n")
- end
-
- def to_s
- return "%s[%s]" % [@type,@title]
- end
- end
-end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/selector.rb b/lib/puppet/parser/ast/selector.rb
index e5eb3b6f5..d363ab7e4 100644
--- a/lib/puppet/parser/ast/selector.rb
+++ b/lib/puppet/parser/ast/selector.rb
@@ -60,15 +60,5 @@ class Puppet::Parser::AST
return retvalue
end
-
- def tree(indent = 0)
- return [
- @param.tree(indent + 1),
- ((@@indline * indent) + self.typewrap(self.pin)),
- @values.tree(indent + 1)
- ].join("\n")
- end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/tag.rb b/lib/puppet/parser/ast/tag.rb
index 4a2015cde..e2882d2f0 100644
--- a/lib/puppet/parser/ast/tag.rb
+++ b/lib/puppet/parser/ast/tag.rb
@@ -24,5 +24,3 @@ class Puppet::Parser::AST
end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/ast/vardef.rb b/lib/puppet/parser/ast/vardef.rb
index ecc10bd14..1e7f874bc 100644
--- a/lib/puppet/parser/ast/vardef.rb
+++ b/lib/puppet/parser/ast/vardef.rb
@@ -22,20 +22,6 @@ class Puppet::Parser::AST
def each
[@name,@value].each { |child| yield child }
end
-
- def tree(indent = 0)
- return [
- @name.tree(indent + 1),
- ((@@indline * 4 * indent) + self.typewrap(self.pin)),
- @value.tree(indent + 1)
- ].join("\n")
- end
-
- def to_s
- return "%s => %s" % [@name,@value]
- end
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/collector.rb b/lib/puppet/parser/collector.rb
index 6c49c6d57..3eb37dfa2 100644
--- a/lib/puppet/parser/collector.rb
+++ b/lib/puppet/parser/collector.rb
@@ -81,20 +81,20 @@ class Puppet::Parser::Collector
# If there are no more resources to find, delete this from the list
# of collections.
if @resources.empty?
- @scope.collections.delete(self)
+ @scope.compile.delete_collection(self)
end
return result
end
- # Collect just virtual objects, from our local configuration.
+ # Collect just virtual objects, from our local compile.
def collect_virtual(exported = false)
if exported
method = :exported?
else
method = :virtual?
end
- scope.resources.find_all do |resource|
+ scope.compile.resources.find_all do |resource|
resource.type == @type and resource.send(method) and match?(resource)
end
end
@@ -117,13 +117,6 @@ class Puppet::Parser::Collector
return objects
end
end
-
-# if objects and ! objects.empty?
-# objects.each { |r| r.virtual = false }
-# return objects
-# else
-# return false
-# end
end
def initialize(scope, type, equery, vquery, form)
@@ -169,7 +162,7 @@ class Puppet::Parser::Collector
# XXX Because the scopes don't expect objects to return values,
# we have to manually add our objects to the scope. This is
# ber-lame.
- scope.setresource(resource)
+ scope.compile.store_resource(scope, resource)
rescue => detail
if Puppet[:trace]
puts detail.backtrace
@@ -181,5 +174,3 @@ class Puppet::Parser::Collector
return resource
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/compile.rb b/lib/puppet/parser/compile.rb
new file mode 100644
index 000000000..6aeebeaae
--- /dev/null
+++ b/lib/puppet/parser/compile.rb
@@ -0,0 +1,498 @@
+# Created by Luke A. Kanies on 2007-08-13.
+# Copyright (c) 2007. All rights reserved.
+
+require 'puppet/external/gratr/digraph'
+require 'puppet/external/gratr/import'
+require 'puppet/external/gratr/dot'
+
+require 'puppet/node'
+require 'puppet/node/configuration'
+require 'puppet/util/errors'
+
+# Maintain a graph of scopes, along with a bunch of data
+# about the individual configuration we're compiling.
+class Puppet::Parser::Compile
+ include Puppet::Util
+ include Puppet::Util::Errors
+ attr_reader :topscope, :parser, :node, :facts, :collections, :configuration
+
+ attr_writer :ast_nodes
+
+ # Add a collection to the global list.
+ def add_collection(coll)
+ @collections << coll
+ end
+
+ # Do we use nodes found in the code, vs. the external node sources?
+ def ast_nodes?
+ defined?(@ast_nodes) and @ast_nodes
+ end
+
+ # Store the fact that we've evaluated a class, and store a reference to
+ # the scope in which it was evaluated, so that we can look it up later.
+ def class_set(name, scope)
+ if existing = @class_scopes[name]
+ if existing.nodescope? or scope.nodescope?
+ raise Puppet::ParseError, "Cannot have classes, nodes, or definitions with the same name"
+ else
+ raise Puppet::DevError, "Somehow evaluated the same class twice"
+ end
+ end
+ @class_scopes[name] = scope
+ @configuration.add_class(name) unless name == ""
+ end
+
+ # Return the scope associated with a class. This is just here so
+ # that subclasses can set their parent scopes to be the scope of
+ # their parent class, and it's also used when looking up qualified
+ # variables.
+ def class_scope(klass)
+ # They might pass in either the class or class name
+ if klass.respond_to?(:classname)
+ @class_scopes[klass.classname]
+ else
+ @class_scopes[klass]
+ end
+ end
+
+ # Return a list of all of the defined classes.
+ def classlist
+ return @configuration.classes
+ end
+
+ # Compile our configuration. This mostly revolves around finding and evaluating classes.
+ # This is the main entry into our configuration.
+ def compile
+ # Set the client's parameters into the top scope.
+ set_node_parameters()
+
+ evaluate_main()
+
+ evaluate_ast_node()
+
+ evaluate_node_classes()
+
+ evaluate_generators()
+
+ fail_on_unevaluated()
+
+ finish()
+
+ if Puppet[:storeconfigs]
+ store()
+ end
+
+ return @configuration
+ end
+
+ # FIXME There are no tests for this.
+ def delete_collection(coll)
+ @collections.delete(coll) if @collections.include?(coll)
+ end
+
+ # FIXME There are no tests for this.
+ def delete_resource(resource)
+ @resource_table.delete(resource.ref) if @resource_table.include?(resource.ref)
+ end
+
+ # Return the node's environment.
+ def environment
+ unless defined? @environment
+ if node.environment and node.environment != ""
+ @environment = node.environment
+ else
+ @environment = nil
+ end
+ end
+ @environment
+ end
+
+ # Evaluate all of the classes specified by the node.
+ def evaluate_node_classes
+ evaluate_classes(@node.classes, @topscope)
+ end
+
+ # Evaluate each specified class in turn. If there are any classes we can't
+ # find, just tag the configuration and move on. This method really just
+ # creates resource objects that point back to the classes, and then the
+ # resources are themselves evaluated later in the process.
+ def evaluate_classes(classes, scope)
+ unless scope.source
+ raise Puppet::DevError, "No source for scope passed to evaluate_classes"
+ end
+ found = []
+ classes.each do |name|
+ # If we can find the class, then make a resource that will evaluate it.
+ if klass = scope.findclass(name)
+ # Create a resource to model this class, and then add it to the list
+ # of resources.
+ resource = Puppet::Parser::Resource.new(:type => "class", :title => klass.classname, :scope => scope, :source => scope.source)
+ store_resource(scope, resource)
+ @configuration.tag(klass.classname)
+ found << name
+ else
+ Puppet.info "Could not find class %s for %s" % [name, node.name]
+ @configuration.tag(name)
+ end
+ end
+ found
+ end
+
+ # Return a resource by either its ref or its type and title.
+ def findresource(string, name = nil)
+ if name
+ string = "%s[%s]" % [string.capitalize, name]
+ end
+
+ @resource_table[string]
+ end
+
+ # Set up our compile. We require a parser
+ # and a node object; the parser is so we can look up classes
+ # and AST nodes, and the node has all of the client's info,
+ # like facts and environment.
+ def initialize(node, parser, options = {})
+ @node = node
+ @parser = parser
+
+ options.each do |param, value|
+ begin
+ send(param.to_s + "=", value)
+ rescue NoMethodError
+ raise ArgumentError, "Compile objects do not accept %s" % param
+ end
+ end
+
+ initvars()
+ init_main()
+ end
+
+ # Create a new scope, with either a specified parent scope or
+ # using the top scope. Adds an edge between the scope and
+ # its parent to the graph.
+ def newscope(parent, options = {})
+ parent ||= @topscope
+ options[:compile] = self
+ options[:parser] ||= self.parser
+ scope = Puppet::Parser::Scope.new(options)
+ @scope_graph.add_edge!(parent, scope)
+ scope
+ end
+
+ # Find the parent of a given scope. Assumes scopes only ever have
+ # one in edge, which will always be true.
+ def parent(scope)
+ if ary = @scope_graph.adjacent(scope, :direction => :in) and ary.length > 0
+ ary[0]
+ else
+ nil
+ end
+ end
+
+ # Return any overrides for the given resource.
+ def resource_overrides(resource)
+ @resource_overrides[resource.ref]
+ end
+
+ # Return a list of all resources.
+ def resources
+ @resource_table.values
+ end
+
+ # Store a resource override.
+ def store_override(override)
+ override.override = true
+
+ # If possible, merge the override in immediately.
+ if resource = @resource_table[override.ref]
+ resource.merge(override)
+ else
+ # Otherwise, store the override for later; these
+ # get evaluated in Resource#finish.
+ @resource_overrides[override.ref] << override
+ end
+ end
+
+ # Store a resource in our resource table.
+ def store_resource(scope, resource)
+ # This might throw an exception
+ verify_uniqueness(resource)
+
+ # Store it in the global table.
+ @resource_table[resource.ref] = resource
+
+ # And in the resource graph. At some point, this might supercede
+ # the global resource table, but the table is a lot faster
+ # so it makes sense to maintain for now.
+ @configuration.add_edge!(scope.resource, resource)
+ end
+
+ private
+
+ # If ast nodes are enabled, then see if we can find and evaluate one.
+ def evaluate_ast_node
+ return unless ast_nodes?
+
+ # Now see if we can find the node.
+ astnode = nil
+ @node.names.each do |name|
+ break if astnode = @parser.nodes[name.to_s.downcase]
+ end
+
+ unless astnode
+ astnode = @parser.nodes["default"]
+ end
+ unless astnode
+ raise Puppet::ParseError, "Could not find default node or by name with '%s'" % node.names.join(", ")
+ end
+
+ # Create a resource to model this node, and then add it to the list
+ # of resources.
+ resource = Puppet::Parser::Resource.new(:type => "node", :title => astnode.classname, :scope => topscope, :source => topscope.source)
+ store_resource(topscope, resource)
+ @configuration.tag(astnode.classname)
+ end
+
+ # Evaluate our collections and return true if anything returned an object.
+ # The 'true' is used to continue a loop, so it's important.
+ def evaluate_collections
+ return false if @collections.empty?
+
+ found_something = false
+ exceptwrap do
+ @collections.each do |collection|
+ if collection.evaluate
+ found_something = true
+ end
+ end
+ end
+
+ return found_something
+ end
+
+ # Make sure all of our resources have been evaluated into native resources.
+ # We return true if any resources have, so that we know to continue the
+ # evaluate_generators loop.
+ def evaluate_definitions
+ exceptwrap do
+ if ary = unevaluated_resources
+ ary.each do |resource|
+ resource.evaluate
+ end
+ # If we evaluated, let the loop know.
+ return true
+ else
+ return false
+ end
+ end
+ end
+
+ # Iterate over collections and resources until we're sure that the whole
+ # compile is evaluated. This is necessary because both collections
+ # and defined resources can generate new resources, which themselves could
+ # be defined resources.
+ def evaluate_generators
+ count = 0
+ loop do
+ done = true
+
+ # Call collections first, then definitions.
+ done = false if evaluate_collections
+ done = false if evaluate_definitions
+ break if done
+ if count > 1000
+ raise Puppet::ParseError, "Somehow looped more than 1000 times while evaluating host configuration"
+ end
+ end
+ end
+
+ # Find and evaluate our main object, if possible.
+ def evaluate_main
+ @main = @parser.findclass("", "") || @parser.newclass("")
+ @topscope.source = @main
+ @main_resource = Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => @topscope, :source => @main)
+ @topscope.resource = @main_resource
+
+ @configuration.add_vertex!(@main_resource)
+
+ @resource_table["Class[main]"] = @main_resource
+ end
+
+ # Make sure the entire configuration is evaluated.
+ def fail_on_unevaluated
+ fail_on_unevaluated_overrides
+ fail_on_unevaluated_resource_collections
+ end
+
+ # If there are any resource overrides remaining, then we could
+ # not find the resource they were supposed to override, so we
+ # want to throw an exception.
+ def fail_on_unevaluated_overrides
+ remaining = []
+ @resource_overrides.each do |name, overrides|
+ remaining += overrides
+ end
+
+ unless remaining.empty?
+ fail Puppet::ParseError,
+ "Could not find object(s) %s" % remaining.collect { |o|
+ o.ref
+ }.join(", ")
+ end
+ end
+
+ # Make sure we don't have any remaining collections that specifically
+ # look for resources, because we want to consider those to be
+ # parse errors.
+ def fail_on_unevaluated_resource_collections
+ remaining = []
+ @collections.each do |coll|
+ # We're only interested in the 'resource' collections,
+ # which result from direct calls of 'realize'. Anything
+ # else is allowed not to return resources.
+ # Collect all of them, so we have a useful error.
+ if r = coll.resources
+ if r.is_a?(Array)
+ remaining += r
+ else
+ remaining << r
+ end
+ end
+ end
+
+ unless remaining.empty?
+ raise Puppet::ParseError, "Failed to realize virtual resources %s" %
+ remaining.join(', ')
+ end
+ end
+
+ # Make sure all of our resources and such have done any last work
+ # necessary.
+ def finish
+ @resource_table.each { |name, resource| resource.finish if resource.respond_to?(:finish) }
+ end
+
+ # Initialize the top-level scope, class, and resource.
+ def init_main
+ # Create our initial scope and a resource that will evaluate main.
+ @topscope = Puppet::Parser::Scope.new(:compile => self, :parser => self.parser)
+ @scope_graph.add_vertex!(@topscope)
+ end
+
+ # Set up all of our internal variables.
+ def initvars
+ # The table for storing class singletons. This will only actually
+ # be used by top scopes and node scopes.
+ @class_scopes = {}
+
+ # The table for all defined resources.
+ @resource_table = {}
+
+ # The list of objects that will available for export.
+ @exported_resources = {}
+
+ # The list of overrides. This is used to cache overrides on objects
+ # that don't exist yet. We store an array of each override.
+ @resource_overrides = Hash.new do |overs, ref|
+ overs[ref] = []
+ end
+
+ # The list of collections that have been created. This is a global list,
+ # but they each refer back to the scope that created them.
+ @collections = []
+
+ # A list of tags we've generated; most class names.
+ @tags = []
+
+ # A graph for maintaining scope relationships.
+ @scope_graph = GRATR::Digraph.new
+ @scope_graph.add_vertex!(@topscope)
+
+ # For maintaining the relationship between scopes and their resources.
+ @configuration = Puppet::Node::Configuration.new(@node.name)
+ @configuration.version = @parser.version
+ end
+
+ # Set the node's parameters into the top-scope as variables.
+ def set_node_parameters
+ node.parameters.each do |param, value|
+ @topscope.setvar(param, value)
+ end
+ end
+
+ # Store the configuration into the database.
+ def store
+ unless Puppet.features.rails?
+ raise Puppet::Error,
+ "storeconfigs is enabled but rails is unavailable"
+ end
+
+ unless ActiveRecord::Base.connected?
+ Puppet::Rails.connect
+ end
+
+ # We used to have hooks here for forking and saving, but I don't
+ # think it's worth retaining at this point.
+ store_to_active_record(@node, @resource_table.values)
+ end
+
+ # Do the actual storage.
+ def store_to_active_record(node, resources)
+ begin
+ # We store all of the objects, even the collectable ones
+ benchmark(:info, "Stored configuration for #{node.name}") do
+ Puppet::Rails::Host.transaction do
+ Puppet::Rails::Host.store(node, resources)
+ end
+ end
+ rescue => detail
+ if Puppet[:trace]
+ puts detail.backtrace
+ end
+ Puppet.err "Could not store configs: %s" % detail.to_s
+ end
+ end
+
+ # Return an array of all of the unevaluated resources. These will be definitions,
+ # which need to get evaluated into native resources.
+ def unevaluated_resources
+ ary = @resource_table.find_all do |name, object|
+ ! object.builtin? and ! object.evaluated?
+ end.collect { |name, object| object }
+
+ if ary.empty?
+ return nil
+ else
+ return ary
+ end
+ end
+
+ # Verify that the given resource isn't defined elsewhere.
+ def verify_uniqueness(resource)
+ # Short-curcuit the common case,
+ unless existing_resource = @resource_table[resource.ref]
+ return true
+ end
+
+ if typeclass = Puppet::Type.type(resource.type) and ! typeclass.isomorphic?
+ Puppet.info "Allowing duplicate %s" % typeclass.name
+ return true
+ end
+
+ # Either it's a defined type, which are never
+ # isomorphic, or it's a non-isomorphic type, so
+ # we should throw an exception.
+ msg = "Duplicate definition: %s is already defined" % resource.ref
+
+ if existing_resource.file and existing_resource.line
+ msg << " in file %s at line %s" %
+ [existing_resource.file, existing_resource.line]
+ end
+
+ if resource.line or resource.file
+ msg << "; cannot redefine"
+ end
+
+ raise Puppet::ParseError.new(msg)
+ end
+end
diff --git a/lib/puppet/parser/configuration.rb b/lib/puppet/parser/configuration.rb
deleted file mode 100644
index c7979e51f..000000000
--- a/lib/puppet/parser/configuration.rb
+++ /dev/null
@@ -1,133 +0,0 @@
-# Created by Luke A. Kanies on 2007-08-13.
-# Copyright (c) 2007. All rights reserved.
-
-require 'puppet/external/gratr/digraph'
-require 'puppet/external/gratr/import'
-require 'puppet/external/gratr/dot'
-
-# Maintain a graph of scopes, along with a bunch of data
-# about the individual configuration we're compiling.
-class Puppet::Parser::Configuration
- attr_reader :topscope, :interpreter, :host, :facts
-
- # Add a collection to the global list.
- def add_collection(coll)
- @collections << coll
- end
-
- # Store the fact that we've evaluated a class, and store a reference to
- # the scope in which it was evaluated, so that we can look it up later.
- def class_set(name, scope)
- @class_scopes[name] = scope
- end
-
- # Return the scope associated with a class. This is just here so
- # that subclasses can set their parent scopes to be the scope of
- # their parent class, and it's also used when looking up qualified
- # variables.
- def class_scope(klass)
- # They might pass in either the class or class name
- if klass.respond_to?(:classname)
- @class_scopes[klass.classname]
- else
- @class_scopes[klass]
- end
- end
-
- # Return a list of all of the defined classes.
- def classlist
- return @class_scopes.keys.reject { |k| k == "" }
- end
-
- # Should the scopes behave declaratively?
- def declarative?
- true
- end
-
- # Set up our configuration. We require an interpreter
- # and a host name, and we normally are passed facts, too.
- def initialize(options)
- @interpreter = options[:interpreter] or
- raise ArgumentError, "You must pass an interpreter to the configuration"
- @facts = options[:facts] || {}
- @host = options[:host] or
- raise ArgumentError, "You must pass a host name to the configuration"
-
- # Call the setup methods from the base class.
- super()
-
- initvars()
- end
-
- # Create a new scope, with either a specified parent scope or
- # using the top scope. Adds an edge between the scope and
- # its parent to the graph.
- def newscope(parent = nil)
- parent ||= @topscope
- scope = Puppet::Parser::Scope.new(:configuration => self)
- @graph.add_edge!(parent, scope)
- scope
- end
-
- # Find the parent of a given scope. Assumes scopes only ever have
- # one in edge, which will always be true.
- def parent(scope)
- if ary = @graph.adjacent(scope, :direction => :in) and ary.length > 0
- ary[0]
- else
- nil
- end
- end
-
- # Return an array of all of the unevaluated objects
- def unevaluated
- ary = @definedtable.find_all do |name, object|
- ! object.builtin? and ! object.evaluated?
- end.collect { |name, object| object }
-
- if ary.empty?
- return nil
- else
- return ary
- end
- end
-
- private
-
- # Set up all of our internal variables.
- def initvars
- # The table for storing class singletons. This will only actually
- # be used by top scopes and node scopes.
- @class_scopes = {}
-
- # The table for all defined resources.
- @resource_table = {}
-
- # The list of objects that will available for export.
- @exported_resources = {}
-
- # The list of overrides. This is used to cache overrides on objects
- # that don't exist yet. We store an array of each override.
- @resource_overrides = Hash.new do |overs, ref|
- overs[ref] = []
- end
-
- # The list of collections that have been created. This is a global list,
- # but they each refer back to the scope that created them.
- @collections = []
-
- # Create our initial scope, our scope graph, and add the initial scope to the graph.
- @topscope = Puppet::Parser::Scope.new(:configuration => self, :type => "main", :name => "top")
- @graph = GRATR::Digraph.new
- @graph.add_vertex!(@topscope)
- end
-
- # Return the list of remaining overrides.
- def overrides
- @resource_overrides.values.flatten
- end
-
- def resources
- @resourcetable
- end
-end
diff --git a/lib/puppet/parser/functions.rb b/lib/puppet/parser/functions.rb
index 946501154..7ffdb6ccb 100644
--- a/lib/puppet/parser/functions.rb
+++ b/lib/puppet/parser/functions.rb
@@ -1,4 +1,3 @@
-# Grr
require 'puppet/util/autoload'
require 'puppet/parser/scope'
@@ -109,7 +108,8 @@ module Functions
# Include the specified classes
newfunction(:include, :doc => "Evaluate one or more classes.") do |vals|
- klasses = evalclasses(*vals)
+ vals = [vals] unless vals.is_a?(Array)
+ klasses = compile.evaluate_classes(vals, self)
missing = vals.find_all do |klass|
! klasses.include?(klass)
@@ -135,7 +135,7 @@ module Functions
newfunction(:tag, :doc => "Add the specified tags to the containing class
or definition. All contained objects will then acquire that tag, also.
") do |vals|
- self.tag(*vals)
+ self.resource.tag(*vals)
end
# Test whether a given tag is set. This functions as a big OR -- if any of the
@@ -144,11 +144,12 @@ module Functions
tells you whether the current container is tagged with the specified tags.
The tags are ANDed, so that all of the specified tags must be included for
the function to return true.") do |vals|
- classlist = self.classlist
+ configtags = compile.configuration.tags
+ resourcetags = resource.tags
retval = true
vals.each do |val|
- unless classlist.include?(val) or self.tags.include?(val)
+ unless configtags.include?(val) or resourcetags.include?(val)
retval = false
break
end
@@ -234,7 +235,7 @@ module Functions
vals = [vals] unless vals.is_a?(Array)
coll.resources = vals
- newcollection(coll)
+ compile.add_collection(coll)
end
newfunction(:search, :doc => "Add another namespace for this class to search.
diff --git a/lib/puppet/parser/grammar.ra b/lib/puppet/parser/grammar.ra
index c7216186a..26cb42217 100644
--- a/lib/puppet/parser/grammar.ra
+++ b/lib/puppet/parser/grammar.ra
@@ -116,19 +116,19 @@ resourcerefs: resourceref
resource: classname LBRACE resourceinstances endsemi RBRACE {
array = val[2]
- if array.instance_of?(AST::ResourceInst)
+ if array.instance_of?(AST::ResourceInstance)
array = [array]
end
result = ast AST::ASTArray
# this iterates across each specified resourceinstance
array.each { |instance|
- unless instance.instance_of?(AST::ResourceInst)
+ unless instance.instance_of?(AST::ResourceInstance)
raise Puppet::Dev, "Got something that isn't an instance"
end
# now, i need to somehow differentiate between those things with
# arrays in their names, and normal things
- result.push ast(AST::ResourceDef,
+ result.push ast(AST::Resource,
:type => val[0],
:title => instance[0],
:params => instance[1])
@@ -248,12 +248,12 @@ colllval: variable
| name
resourceinst: resourcename COLON params endcomma {
- result = ast AST::ResourceInst, :children => [val[0],val[2]]
+ result = ast AST::ResourceInstance, :children => [val[0],val[2]]
}
resourceinstances: resourceinst
| resourceinstances SEMIC resourceinst {
- if val[0].instance_of?(AST::ResourceInst)
+ if val[0].instance_of?(AST::ResourceInstance)
result = ast AST::ASTArray, :children => [val[0],val[2]]
else
val[0].push val[2]
@@ -385,9 +385,9 @@ boolean: BOOLEAN {
resourceref: NAME LBRACK rvalue RBRACK {
Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized")
- result = ast AST::ResourceRef, :type => val[0], :title => val[2]
+ result = ast AST::ResourceReference, :type => val[0], :title => val[2]
} | classref LBRACK rvalue RBRACK {
- result = ast AST::ResourceRef, :type => val[0], :title => val[2]
+ result = ast AST::ResourceReference, :type => val[0], :title => val[2]
}
ifstatement: IF iftest LBRACE statements RBRACE else {
diff --git a/lib/puppet/parser/interpreter.rb b/lib/puppet/parser/interpreter.rb
index 18bf31087..a4ea26572 100644
--- a/lib/puppet/parser/interpreter.rb
+++ b/lib/puppet/parser/interpreter.rb
@@ -3,314 +3,43 @@ require 'timeout'
require 'puppet/rails'
require 'puppet/util/methodhelper'
require 'puppet/parser/parser'
+require 'puppet/parser/compile'
require 'puppet/parser/scope'
-# The interpreter's job is to convert from a parsed file to the configuration
-# for a given client. It really doesn't do any work on its own, it just collects
-# and calls out to other objects.
+# The interpreter is a very simple entry-point class that
+# manages the existence of the parser (e.g., replacing it
+# when files are reparsed). You can feed it a node and
+# get the node's configuration back.
class Puppet::Parser::Interpreter
- class NodeDef
- include Puppet::Util::MethodHelper
- attr_accessor :name, :classes, :parameters, :source
-
- def evaluate(options)
- begin
- parameters.each do |param, value|
- # Don't try to override facts with these parameters
- options[:scope].setvar(param, value) unless options[:scope].lookupvar(param, false) != :undefined
- end
-
- # Also, set the 'nodename', since it might not be obvious how the node was looked up
- options[:scope].setvar("nodename", @name) unless options[:scope].lookupvar(@nodename, false) != :undefined
- rescue => detail
- raise Puppet::ParseError, "Could not set parameters for %s: %s" % [name, detail]
- end
-
- # Then evaluate the classes.
- begin
- options[:scope].function_include(classes.find_all { |c| options[:scope].findclass(c) })
- rescue => detail
- puts detail.backtrace
- raise Puppet::ParseError, "Could not evaluate classes for %s: %s" % [name, detail]
- end
- end
-
- def initialize(args)
- set_options(args)
-
- raise Puppet::DevError, "NodeDefs require names" unless self.name
-
- if self.classes.is_a?(String)
- @classes = [@classes]
- else
- @classes ||= []
- end
- @parameters ||= {}
- end
-
- def safeevaluate(args)
- evaluate(args)
- end
- end
-
include Puppet::Util
attr_accessor :usenodes
-
- class << self
- attr_writer :ldap
- end
-
- # just shorten the constant path a bit, using what amounts to an alias
- AST = Puppet::Parser::AST
+ attr_accessor :code, :file
include Puppet::Util::Errors
- # Create an ldap connection. This is a class method so others can call
- # it and use the same variables and such.
- def self.ldap
- unless defined? @ldap and @ldap
- if Puppet[:ldapssl]
- @ldap = LDAP::SSLConn.new(Puppet[:ldapserver], Puppet[:ldapport])
- elsif Puppet[:ldaptls]
- @ldap = LDAP::SSLConn.new(
- Puppet[:ldapserver], Puppet[:ldapport], true
- )
- else
- @ldap = LDAP::Conn.new(Puppet[:ldapserver], Puppet[:ldapport])
- end
- @ldap.set_option(LDAP::LDAP_OPT_PROTOCOL_VERSION, 3)
- @ldap.set_option(LDAP::LDAP_OPT_REFERRALS, LDAP::LDAP_OPT_ON)
- @ldap.simple_bind(Puppet[:ldapuser], Puppet[:ldappassword])
- end
-
- return @ldap
- end
-
- # Make sure we don't have any remaining collections that specifically
- # look for resources, because we want to consider those to be
- # parse errors.
- def check_resource_collections(scope)
- remaining = []
- scope.collections.each do |coll|
- if r = coll.resources
- if r.is_a?(Array)
- remaining += r
- else
- remaining << r
- end
- end
- end
- unless remaining.empty?
- raise Puppet::ParseError, "Failed to find virtual resources %s" %
- remaining.join(', ')
- end
- end
-
- # Iteratively evaluate all of the objects. This finds all of the objects
- # that represent definitions and evaluates the definitions appropriately.
- # It also adds defaults and overrides as appropriate.
- def evaliterate(scope)
- count = 0
- loop do
- count += 1
- done = true
- # First perform collections, so we can collect defined types.
- if coll = scope.collections and ! coll.empty?
- exceptwrap do
- coll.each do |c|
- # Only keep the loop going if we actually successfully
- # collected something.
- if o = c.evaluate
- done = false
- end
- end
- end
- end
-
- # Then evaluate any defined types.
- if ary = scope.unevaluated
- ary.each do |resource|
- resource.evaluate
- end
- # If we evaluated, then loop through again.
- done = false
- end
- break if done
-
- if count > 1000
- raise Puppet::ParseError, "Got 1000 class levels, which is unsupported"
- end
- end
- end
-
- # Evaluate a specific node.
- def evalnode(client, scope, facts)
- return unless self.usenodes
-
- unless client
- raise Puppet::Error,
- "Cannot evaluate nodes with a nil client"
- end
- names = [client]
-
- # Make sure both the fqdn and the short name of the
- # host can be used in the manifest
- if client =~ /\./
- names << client.sub(/\..+/,'')
- else
- names << "#{client}.#{facts['domain']}"
- end
-
- if names.empty?
- raise Puppet::Error,
- "Cannot evaluate nodes with a nil client"
- end
-
- # Look up our node object.
- if nodeclass = nodesearch(*names)
- nodeclass.safeevaluate :scope => scope
- else
- raise Puppet::Error, "Could not find %s with names %s" %
- [client, names.join(", ")]
- end
- end
-
- # Evaluate all of the code we can find that's related to our client.
- def evaluate(client, facts)
- scope = Puppet::Parser::Scope.new(:interp => self) # no parent scope
- scope.name = "top"
- scope.type = "main"
-
- scope.host = client || facts["hostname"] || Facter.value(:hostname)
-
- classes = @classes.dup
-
- # Okay, first things first. Set our facts.
- scope.setfacts(facts)
-
- # Everyone will always evaluate the top-level class, if there is one.
- if klass = findclass("", "")
- # Set the source, so objects can tell where they were defined.
- scope.source = klass
- klass.safeevaluate :scope => scope, :nosubscope => true
- end
-
- # Next evaluate the node. We pass the facts so they can be used
- # when building the list of names for which to search.
- evalnode(client, scope, facts)
-
- # If we were passed any classes, evaluate those.
- if classes
- classes.each do |klass|
- if klassobj = findclass("", klass)
- klassobj.safeevaluate :scope => scope
- end
- end
- end
-
- # That was the first pass evaluation. Now iteratively evaluate
- # until we've gotten rid of all of everything or thrown an error.
- evaliterate(scope)
-
- # Now make sure we fail if there's anything left to do
- failonleftovers(scope)
-
- # Now finish everything. This recursively calls finish on the
- # contained scopes and resources.
- scope.finish
-
- # Store everything. We need to do this before translation, because
- # it operates on resources, not transobjects.
- if Puppet[:storeconfigs]
- args = {
- :resources => scope.resources,
- :name => scope.host,
- :facts => facts
- }
- unless scope.classlist.empty?
- args[:classes] = scope.classlist
- end
-
- storeconfigs(args)
- end
-
- # Now, finally, convert our scope tree + resources into a tree of
- # buckets and objects.
- objects = scope.translate
-
- # Add the class list
- unless scope.classlist.empty?
- objects.classes = scope.classlist
- end
-
- return objects
+ # Determine the configuration version for a given node's environment.
+ def configuration_version(node)
+ parser(node.environment).version
end
- # Fail if there any overrides left to perform.
- def failonleftovers(scope)
- overrides = scope.overrides
- unless overrides.empty?
- fail Puppet::ParseError,
- "Could not find object(s) %s" % overrides.collect { |o|
- o.ref
- }.join(", ")
- end
-
- # Now check that there aren't any extra resource collections.
- check_resource_collections(scope)
- end
-
- # Create proxy methods, so the scopes can call the interpreter, since
- # they don't have access to the parser.
- def findclass(namespace, name)
- @parser.findclass(namespace, name)
- end
- def finddefine(namespace, name)
- @parser.finddefine(namespace, name)
+ # evaluate our whole tree
+ def compile(node)
+ return Puppet::Parser::Compile.new(node, parser(node.environment), :ast_nodes => usenodes?).compile
end
# create our interpreter
- def initialize(hash)
- if @code = hash[:Code]
- @file = nil # to avoid warnings
- elsif ! @file = hash[:Manifest]
- devfail "You must provide code or a manifest"
+ def initialize(options = {})
+ if @code = options[:Code]
+ elsif @file = options[:Manifest]
end
- if hash.include?(:UseNodes)
- @usenodes = hash[:UseNodes]
+ if options.include?(:UseNodes)
+ @usenodes = options[:UseNodes]
else
@usenodes = true
end
-
- if Puppet[:ldapnodes]
- # Nodes in the file override nodes in ldap.
- @nodesource = :ldap
- elsif Puppet[:external_nodes] != "none"
- @nodesource = :external
- else
- # By default, we only search for parsed nodes.
- @nodesource = :code
- end
-
- @setup = false
-
- # Set it to either the value or nil. This is currently only used
- # by the cfengine module.
- @classes = hash[:Classes] || []
-
- @local = hash[:Local] || false
-
- if hash.include?(:ForkSave)
- @forksave = hash[:ForkSave]
- else
- # This is just too dangerous right now. Sorry, it's going
- # to have to be slow.
- @forksave = false
- end
-
# The class won't always be defined during testing.
if Puppet[:storeconfigs]
if Puppet.features.rails?
@@ -320,231 +49,57 @@ class Puppet::Parser::Interpreter
end
end
- @files = []
-
- # Create our parser object
- parsefiles
- end
-
- # Pass these methods through to the parser.
- [:newclass, :newdefine, :newnode].each do |name|
- define_method(name) do |*args|
- @parser.send(name, *args)
- end
- end
-
- # Add a new file to be checked when we're checking to see if we should be
- # reparsed.
- def newfile(*files)
- files.each do |file|
- unless file.is_a? Puppet::Util::LoadedFile
- file = Puppet::Util::LoadedFile.new(file)
- end
- @files << file
- end
- end
-
- # Search for our node in the various locations.
- def nodesearch(*nodes)
- nodes = nodes.collect { |n| n.to_s.downcase }
-
- method = "nodesearch_%s" % @nodesource
- # Do an inverse sort on the length, so the longest match always
- # wins
- nodes.sort { |a,b| b.length <=> a.length }.each do |node|
- node = node.to_s if node.is_a?(Symbol)
- if obj = self.send(method, node)
- if obj.is_a?(AST::Node)
- nsource = obj.file
- else
- nsource = obj.source
- end
- Puppet.info "Found %s in %s" % [node, nsource]
- return obj
- end
- end
-
- # If they made it this far, we haven't found anything, so look for a
- # default node.
- unless nodes.include?("default")
- if defobj = self.nodesearch("default")
- Puppet.notice "Using default node for %s" % [nodes[0]]
- return defobj
- end
- end
-
- return nil
+ @parsers = {}
end
- # See if our node was defined in the code.
- def nodesearch_code(name)
- @parser.nodes[name]
- end
-
- def parsedate
- parsefiles()
- @parsedate
- end
-
- # evaluate our whole tree
- def run(client, facts)
- # We have to leave this for after initialization because there
- # seems to be a problem keeping ldap open after a fork.
- unless @setup
- method = "setup_%s" % @nodesource.to_s
- if respond_to? method
- exceptwrap :type => Puppet::Error,
- :message => "Could not set up node source %s" % @nodesource do
- self.send(method)
- end
- end
- end
- parsefiles()
-
- # Evaluate all of the appropriate code.
- objects = evaluate(client, facts)
-
- # And return it all.
- return objects
- end
-
- # Connect to the LDAP Server
- def setup_ldap
- self.class.ldap = nil
- unless Puppet.features.ldap?
- Puppet.notice(
- "Could not set up LDAP Connection: Missing ruby/ldap libraries"
- )
- @ldap = nil
- return
- end
-
- begin
- @ldap = self.class.ldap()
- rescue => detail
- raise Puppet::Error, "Could not connect to LDAP: %s" % detail
- end
- end
-
- def scope
- return @scope
+ # Should we parse ast nodes?
+ def usenodes?
+ defined?(@usenodes) and @usenodes
end
private
- # Check whether any of our files have changed.
- def checkfiles
- if @files.find { |f| f.changed? }
- @parsedate = Time.now.to_i
- end
- end
-
- # Parse the files, generating our parse tree. This automatically
- # reparses only if files are updated, so it's safe to call multiple
- # times.
- def parsefiles
- # First check whether there are updates to any non-puppet files
- # like templates. If we need to reparse, this will get quashed,
- # but it needs to be done first in case there's no reparse
- # but there are other file changes.
- checkfiles()
-
- # Check if the parser should reparse.
- if @file
- if defined? @parser
- if stamp = @parser.reparse?
- Puppet.notice "Reloading files"
- else
- return false
- end
- end
-
- unless FileTest.exists?(@file)
- # If we've already parsed, then we're ok.
- if findclass("", "")
- return
- else
- raise Puppet::Error, "Manifest %s must exist" % @file
- end
- end
- end
-
- # Create a new parser, just to keep things fresh. Don't replace our
- # current parser until we know weverything works.
- newparser = Puppet::Parser::Parser.new()
- if @code
- newparser.string = @code
- else
- newparser.file = @file
- end
-
- # Parsing stores all classes and defines and such in their
- # various tables, so we don't worry about the return.
+ # Create a new parser object and pre-parse the configuration.
+ def create_parser(environment)
begin
- if @local
- newparser.parse
+ parser = Puppet::Parser::Parser.new(:environment => environment)
+ if self.code
+ parser.string = self.code
+ elsif self.file
+ parser.file = self.file
else
- benchmark(:info, "Parsed manifest") do
- newparser.parse
- end
- end
- # We've gotten this far, so it's ok to swap the parsers.
- oldparser = @parser
- @parser = newparser
- if oldparser
- oldparser.clear
+ file = Puppet.config.value(:manifest, environment)
+ parser.file = file
end
-
- # Mark when we parsed, so we can check freshness
- @parsedate = Time.now.to_i
+ parser.parse
+ return parser
rescue => detail
if Puppet[:trace]
puts detail.backtrace
end
- Puppet.err "Could not parse; using old configuration: %s" % detail
+ msg = "Could not parse"
+ if environment and environment != ""
+ msg += " for environment %s" % environment
+ end
+ msg += ": %s" % detail
+ raise Puppet::Error, detail
end
end
- # Store the configs into the database.
- def storeconfigs(hash)
- unless Puppet.features.rails?
- raise Puppet::Error,
- "storeconfigs is enabled but rails is unavailable"
- end
-
- unless ActiveRecord::Base.connected?
- Puppet::Rails.connect
- end
-
- # Fork the storage, since we don't need the client waiting
- # on that. How do I avoid this duplication?
- if @forksave
- fork {
- # We store all of the objects, even the collectable ones
- benchmark(:info, "Stored configuration for #{hash[:name]}") do
- # Try to batch things a bit, by putting them into
- # a transaction
- Puppet::Rails::Host.transaction do
- Puppet::Rails::Host.store(hash)
- end
- end
- }
- else
+ # Return the parser for a specific environment.
+ def parser(environment)
+ if ! @parsers[environment] or @parsers[environment].reparse?
+ # This will throw an exception if it does not succeed. We only
+ # want to get rid of the old parser if we successfully create a new
+ # one.
begin
- # We store all of the objects, even the collectable ones
- benchmark(:info, "Stored configuration for #{hash[:name]}") do
- Puppet::Rails::Host.transaction do
- Puppet::Rails::Host.store(hash)
- end
- end
- rescue => detail
- if Puppet[:trace]
- puts detail.backtrace
- end
- Puppet.err "Could not store configs: %s" % detail.to_s
+ tmp = create_parser(environment)
+ @parsers[environment].clear if @parsers[environment]
+ @parsers[environment] = tmp
+ rescue
+ # Nothing, yo.
end
end
+ @parsers[environment]
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/parser.rb b/lib/puppet/parser/parser.rb
index 21a054223..907bddc0a 100644
--- a/lib/puppet/parser/parser.rb
+++ b/lib/puppet/parser/parser.rb
@@ -29,7 +29,7 @@ module Puppet
class Parser < Racc::Parser
-module_eval <<'..end grammar.ra modeval..idc5e5087e93', 'grammar.ra', 640
+module_eval <<'..end grammar.ra modeval..id8b4fcf8e20', 'grammar.ra', 640
# It got too annoying having code in a file that needs to be compiled.
require 'puppet/parser/parser_support'
@@ -41,7 +41,7 @@ require 'puppet/parser/parser_support'
# $Id$
-..end grammar.ra modeval..idc5e5087e93
+..end grammar.ra modeval..id8b4fcf8e20
##### racc 1.4.5 generates ###
@@ -929,19 +929,19 @@ module_eval <<'.,.,', 'grammar.ra', 115
module_eval <<'.,.,', 'grammar.ra', 136
def _reduce_34( val, _values, result )
array = val[2]
- if array.instance_of?(AST::ResourceInst)
+ if array.instance_of?(AST::ResourceInstance)
array = [array]
end
result = ast AST::ASTArray
# this iterates across each specified resourceinstance
array.each { |instance|
- unless instance.instance_of?(AST::ResourceInst)
+ unless instance.instance_of?(AST::ResourceInstance)
raise Puppet::Dev, "Got something that isn't an instance"
end
# now, i need to somehow differentiate between those things with
# arrays in their names, and normal things
- result.push ast(AST::ResourceDef,
+ result.push ast(AST::Resource,
:type => val[0],
:title => instance[0],
:params => instance[1])
@@ -1115,7 +1115,7 @@ module_eval <<'.,.,', 'grammar.ra', 245
module_eval <<'.,.,', 'grammar.ra', 252
def _reduce_57( val, _values, result )
- result = ast AST::ResourceInst, :children => [val[0],val[2]]
+ result = ast AST::ResourceInstance, :children => [val[0],val[2]]
result
end
.,.,
@@ -1124,7 +1124,7 @@ module_eval <<'.,.,', 'grammar.ra', 252
module_eval <<'.,.,', 'grammar.ra', 262
def _reduce_59( val, _values, result )
- if val[0].instance_of?(AST::ResourceInst)
+ if val[0].instance_of?(AST::ResourceInstance)
result = ast AST::ASTArray, :children => [val[0],val[2]]
else
val[0].push val[2]
@@ -1344,14 +1344,14 @@ module_eval <<'.,.,', 'grammar.ra', 384
module_eval <<'.,.,', 'grammar.ra', 389
def _reduce_105( val, _values, result )
Puppet.warning addcontext("Deprecation notice: Resource references should now be capitalized")
- result = ast AST::ResourceRef, :type => val[0], :title => val[2]
+ result = ast AST::ResourceReference, :type => val[0], :title => val[2]
result
end
.,.,
module_eval <<'.,.,', 'grammar.ra', 391
def _reduce_106( val, _values, result )
- result = ast AST::ResourceRef, :type => val[0], :title => val[2]
+ result = ast AST::ResourceReference, :type => val[0], :title => val[2]
result
end
.,.,
diff --git a/lib/puppet/parser/parser_support.rb b/lib/puppet/parser/parser_support.rb
index 728f75a69..8cf0dcfe8 100644
--- a/lib/puppet/parser/parser_support.rb
+++ b/lib/puppet/parser/parser_support.rb
@@ -1,3 +1,5 @@
+# I pulled this into a separate file, because I got
+# tired of rebuilding the parser.rb file all the time.
class Puppet::Parser::Parser
require 'puppet/parser/functions'
@@ -13,7 +15,7 @@ class Puppet::Parser::Parser
AST = Puppet::Parser::AST
- attr_reader :file, :interp
+ attr_reader :version, :environment
attr_accessor :files
@@ -83,6 +85,10 @@ class Puppet::Parser::Parser
raise except
end
+ def file
+ @lexer.file
+ end
+
def file=(file)
unless FileTest.exists?(file)
unless file =~ /\.pp$/
@@ -119,7 +125,7 @@ class Puppet::Parser::Parser
# The recursive method used to actually look these objects up.
def fqfind(namespace, name, table)
namespace = namespace.downcase
- name = name.downcase
+ name = name.to_s.downcase
if name =~ /^::/ or namespace == ""
classname = name.sub(/^::/, '')
unless table[classname]
@@ -174,14 +180,14 @@ class Puppet::Parser::Parser
"in file #{@lexer.file} at line #{@lexer.line}"
)
end
- files = Puppet::Module::find_manifests(pat, dir)
+ files = Puppet::Module::find_manifests(pat, :cwd => dir)
if files.size == 0
raise Puppet::ImportError.new("No file(s) found for import " +
"of '#{pat}'")
end
files.collect { |file|
- parser = Puppet::Parser::Parser.new(@astset)
+ parser = Puppet::Parser::Parser.new(:astset => @astset, :environment => @environment)
parser.files = self.files
Puppet.debug("importing '%s'" % file)
@@ -200,11 +206,10 @@ class Puppet::Parser::Parser
}
end
- def initialize(astset = nil)
+ def initialize(options = {})
+ @astset = options[:astset] || ASTSet.new({}, {}, {})
+ @environment = options[:environment]
initvars()
- if astset
- @astset = astset
- end
end
# Initialize or reset all of our variables.
@@ -212,15 +217,6 @@ class Puppet::Parser::Parser
@lexer = Puppet::Parser::Lexer.new()
@files = []
@loaded = []
-
- # This is where we store our classes and definitions and nodes.
- # Clear each hash, just to help the GC a bit.
- if defined?(@astset)
- [:classes, :definitions, :nodes].each do |name|
- @astset.send(name).clear
- end
- end
- @astset = ASTSet.new({}, {}, {})
end
# Try to load a class, since we could not find it.
@@ -340,7 +336,7 @@ class Puppet::Parser::Parser
args[param] = options[param] if options[param]
end
- @astset.definitions[name] = ast AST::Component, args
+ @astset.definitions[name] = ast AST::Definition, args
end
# Create a new node. Nodes are special, because they're stored in a global
@@ -425,6 +421,7 @@ class Puppet::Parser::Parser
# Store the results as the top-level class.
newclass("", :code => main)
end
+ @version = Time.now.to_i
return @astset
ensure
@lexer.clear
@@ -442,6 +439,16 @@ class Puppet::Parser::Parser
def string=(string)
@lexer.string = string
end
-end
-# $Id$
+ # Add a new file to be checked when we're checking to see if we should be
+ # reparsed. This is basically only used by the TemplateWrapper to let the
+ # parser know about templates that should be parsed.
+ def watch_file(*files)
+ files.each do |file|
+ unless file.is_a? Puppet::Util::LoadedFile
+ file = Puppet::Util::LoadedFile.new(file)
+ end
+ @files << file
+ end
+ end
+end
diff --git a/lib/puppet/parser/resource.rb b/lib/puppet/parser/resource.rb
index 18ec15ac0..a8da6b054 100644
--- a/lib/puppet/parser/resource.rb
+++ b/lib/puppet/parser/resource.rb
@@ -3,16 +3,15 @@
class Puppet::Parser::Resource
require 'puppet/parser/resource/param'
require 'puppet/parser/resource/reference'
- ResParam = Struct.new :name, :value, :source, :line, :file
include Puppet::Util
include Puppet::Util::MethodHelper
include Puppet::Util::Errors
include Puppet::Util::Logging
attr_accessor :source, :line, :file, :scope, :rails_id
- attr_accessor :virtual, :override, :params, :translated
+ attr_accessor :virtual, :override, :translated
- attr_reader :exported
+ attr_reader :exported, :evaluated, :params
attr_writer :tags
@@ -24,7 +23,7 @@ class Puppet::Parser::Resource
end
# Set up some boolean test methods
- [:exported, :translated, :override].each do |method|
+ [:exported, :translated, :override, :virtual, :evaluated].each do |method|
newmeth = (method.to_s + "?").intern
define_method(newmeth) do
self.send(method)
@@ -43,44 +42,6 @@ class Puppet::Parser::Resource
end
end
- # Add default values from our definition.
- def adddefaults
- defaults = scope.lookupdefaults(self.type)
-
- defaults.each do |name, param|
- unless @params.include?(param.name)
- self.debug "Adding default for %s" % param.name
-
- @params[param.name] = param
- end
- end
- end
-
- # Add any metaparams defined in our scope. This actually adds any metaparams
- # from any parent scope, and there's currently no way to turn that off.
- def addmetaparams
- Puppet::Type.eachmetaparam do |name|
- next if self[name]
- if val = scope.lookupvar(name.to_s, false)
- unless val == :undefined
- set Param.new(:name => name, :value => val,
- :source => scope.source)
- end
- end
- end
- end
-
- # Add any overrides for this object.
- def addoverrides
- overrides = scope.lookupoverrides(self)
-
- overrides.each do |over|
- self.merge(over)
- end
-
- overrides.clear
- end
-
def builtin=(bool)
@ref.builtin = bool
end
@@ -89,15 +50,8 @@ class Puppet::Parser::Resource
def evaluate
if klass = @ref.definedtype
finish()
- scope.deleteresource(self)
- return klass.evaluate(:scope => scope,
- :type => self.type,
- :title => self.title,
- :arguments => self.to_hash,
- :scope => self.scope,
- :virtual => self.virtual,
- :exported => self.exported
- )
+ scope.compile.delete_resource(self)
+ return klass.evaluate(:scope => scope, :resource => self)
elsif builtin?
devfail "Cannot evaluate a builtin type"
else
@@ -107,6 +61,8 @@ class Puppet::Parser::Resource
@evaluated = true
end
+ # Mark this resource as both exported and virtual,
+ # or remove the exported mark.
def exported=(value)
if value
@virtual = true
@@ -116,63 +72,79 @@ class Puppet::Parser::Resource
end
end
- def evaluated?
- if defined? @evaluated and @evaluated
- true
- else
- false
- end
- end
-
# Do any finishing work on this object, called before evaluation or
# before storage/translation.
def finish
- addoverrides()
- adddefaults()
- addmetaparams()
+ add_overrides()
+ add_defaults()
+ add_metaparams()
+ validate()
end
def initialize(options)
- options = symbolize_options(options)
-
- # Collect the options necessary to make the reference.
- refopts = [:type, :title].inject({}) do |hash, param|
- hash[param] = options[param]
- options.delete(param)
- hash
+ # Set all of the options we can.
+ options.each do |option, value|
+ if respond_to?(option.to_s + "=")
+ send(option.to_s + "=", value)
+ options.delete(option)
+ end
end
- @params = {}
- tmpparams = nil
- if tmpparams = options[:params]
- options.delete(:params)
+ unless self.scope
+ raise ArgumentError, "Resources require a scope"
end
+ @source ||= scope.source
- # Now set the rest of the options.
- set_options(options)
+ options = symbolize_options(options)
- @ref = Reference.new(refopts)
+ # Set up our reference.
+ if type = options[:type] and title = options[:title]
+ options.delete(:type)
+ options.delete(:title)
+ else
+ raise ArgumentError, "Resources require a type and title"
+ end
- requiredopts(:scope, :source)
+ @ref = Reference.new(:type => type, :title => title, :scope => self.scope)
- @ref.scope = self.scope
+ @params = {}
- if tmpparams
- tmpparams.each do |param|
- # We use the method here, because it does type-checking.
- set(param)
+ # Define all of the parameters
+ if params = options[:params]
+ options.delete(:params)
+ params.each do |param|
+ set_parameter(param)
end
end
+
+ # Throw an exception if we've got any arguments left to set.
+ unless options.empty?
+ raise ArgumentError, "Resources do not accept %s" % options.keys.collect { |k| k.to_s }.join(", ")
+ end
+
+ @tags = []
+ @tags << @ref.type.to_s
+ @tags << @ref.title.to_s if @ref.title.to_s =~ /^[-\w]+$/
+ if scope.resource
+ @tags += scope.resource.tags
+ end
end
- # Merge an override resource in.
+ # Merge an override resource in. This will throw exceptions if
+ # any overrides aren't allowed.
def merge(resource)
+ # Test the resource scope, to make sure the resource is even allowed
+ # to override.
+ unless self.source.object_id == resource.source.object_id || resource.source.child_of?(self.source)
+ raise Puppet::ParseError.new("Only subclasses can override parameters", resource.line, resource.file)
+ end
# Some of these might fail, but they'll fail in the way we want.
resource.params.each do |name, param|
- set(param)
+ override_parameter(param)
end
end
+ # Modify this resource in the Rails database. Poor design, yo.
def modify_rails(db_resource)
args = rails_args
args.each do |param, value|
@@ -215,6 +187,15 @@ class Puppet::Parser::Resource
})
end
+ # Return the resource name, or the title if no name
+ # was specified.
+ def name
+ unless defined? @name
+ @name = self[:name] || self.title
+ end
+ @name
+ end
+
# This *significantly* reduces the number of calls to Puppet.[].
def paramcheck?
unless defined? @@paramcheck
@@ -223,23 +204,6 @@ class Puppet::Parser::Resource
@@paramcheck
end
- # Verify that all passed parameters are valid. This throws an error if
- # there's a problem, so we don't have to worry about the return value.
- def paramcheck(param)
- param = param.to_s
- # Now make sure it's a valid argument to our class. These checks
- # are organized in order of commonhood -- most types, it's a valid
- # argument and paramcheck is enabled.
- if @ref.typeclass.validattr?(param)
- true
- elsif %w{name title}.include?(param) # always allow these
- true
- elsif paramcheck?
- self.fail Puppet::ParseError, "Invalid parameter '%s' for type '%s'" %
- [param.inspect, @ref.type]
- end
- end
-
# A temporary occasion, until I get paths in the scopes figured out.
def path
to_s
@@ -250,65 +214,22 @@ class Puppet::Parser::Resource
@ref.to_s
end
- # You have to pass a Resource::Param to this.
- def set(param, value = nil, source = nil)
- if value and source
- param = Puppet::Parser::Resource::Param.new(
- :name => param, :value => value, :source => source
- )
- elsif ! param.is_a?(Puppet::Parser::Resource::Param)
- raise ArgumentError, "Must pass a parameter or all necessary values"
- end
- # Because definitions are now parse-time, I can paramcheck immediately.
- paramcheck(param.name)
-
- if current = @params[param.name]
- # This is where we'd ignore any equivalent values if we wanted to,
- # but that would introduce a lot of really bad ordering issues.
- if param.source.child_of?(current.source)
- if param.add
- # Merge with previous value.
- param.value = [ current.value, param.value ].flatten
- end
-
- # Replace it, keeping all of its info.
- @params[param.name] = param
- else
- if Puppet[:trace]
- puts caller
- end
- msg = "Parameter '%s' is already set on %s" %
- [param.name, self.to_s]
- if current.source.to_s != ""
- msg += " by %s" % current.source
- end
- if current.file or current.line
- fields = []
- fields << current.file if current.file
- fields << current.line.to_s if current.line
- msg += " at %s" % fields.join(":")
- end
- msg += "; cannot redefine"
- error = Puppet::ParseError.new(msg)
- error.file = param.file if param.file
- error.line = param.line if param.line
- raise error
+ # Add a tag to our current list. These tags will be added to all
+ # of the objects contained in this scope.
+ def tag(*ary)
+ ary.each { |tag|
+ tag = tag.to_s
+ unless tag =~ /^\w[-\w]*$/
+ fail Puppet::ParseError, "Invalid tag %s" % tag.inspect
end
- else
- if self.source == param.source or param.source.child_of?(self.source)
- @params[param.name] = param
- else
- fail Puppet::ParseError, "Only subclasses can set parameters"
+ unless @tags.include?(tag)
+ @tags << tag
end
- end
+ }
end
def tags
- unless defined? @tags
- @tags = scope.tags
- @tags << self.type
- end
- @tags
+ @tags.dup
end
def to_hash
@@ -346,12 +267,26 @@ class Puppet::Parser::Resource
# Translate our object to a transportable object.
def to_trans
- unless builtin?
- devfail "Tried to translate a non-builtin resource"
+ return nil if virtual?
+
+ if builtin?
+ to_transobject
+ else
+ to_transbucket
end
+ end
- return nil if virtual?
+ def to_transbucket
+ bucket = Puppet::TransBucket.new([])
+
+ bucket.type = self.type
+ bucket.name = self.title
+
+ # TransBuckets don't support parameters, which is why they're being deprecated.
+ return bucket
+ end
+ def to_transobject
# Now convert to a transobject
obj = Puppet::TransObject.new(@ref.title, @ref.type)
to_hash.each do |p, v|
@@ -384,12 +319,102 @@ class Puppet::Parser::Resource
return obj
end
-
- def virtual?
- self.virtual
- end
private
+
+ # Add default values from our definition.
+ def add_defaults
+ scope.lookupdefaults(self.type).each do |name, param|
+ unless @params.include?(name)
+ self.debug "Adding default for %s" % name
+
+ @params[name] = param
+ end
+ end
+ end
+
+ # Add any metaparams defined in our scope. This actually adds any metaparams
+ # from any parent scope, and there's currently no way to turn that off.
+ def add_metaparams
+ Puppet::Type.eachmetaparam do |name|
+ # Skip metaparams that we already have defined.
+ next if @params[name]
+ if val = scope.lookupvar(name.to_s, false)
+ unless val == :undefined
+ set_parameter(name, val)
+ end
+ end
+ end
+ end
+
+ # Add any overrides for this object.
+ def add_overrides
+ if overrides = scope.compile.resource_overrides(self)
+ overrides.each do |over|
+ self.merge(over)
+ end
+
+ # Remove the overrides, so that the configuration knows there
+ # are none left.
+ overrides.clear
+ end
+ end
+
+ # Accept a parameter from an override.
+ def override_parameter(param)
+ # This can happen if the override is defining a new parameter, rather
+ # than replacing an existing one.
+ unless current = @params[param.name]
+ @params[param.name] = param
+ return
+ end
+
+ # The parameter is already set. See if they're allowed to override it.
+ if param.source.child_of?(current.source)
+ if param.add
+ # Merge with previous value.
+ param.value = [ current.value, param.value ].flatten
+ end
+
+ # Replace it, keeping all of its info.
+ @params[param.name] = param
+ else
+ if Puppet[:trace]
+ puts caller
+ end
+ msg = "Parameter '%s' is already set on %s" %
+ [param.name, self.to_s]
+ if current.source.to_s != ""
+ msg += " by %s" % current.source
+ end
+ if current.file or current.line
+ fields = []
+ fields << current.file if current.file
+ fields << current.line.to_s if current.line
+ msg += " at %s" % fields.join(":")
+ end
+ msg += "; cannot redefine"
+ raise Puppet::ParseError.new(msg, param.line, param.file)
+ end
+ end
+
+ # Verify that all passed parameters are valid. This throws an error if
+ # there's a problem, so we don't have to worry about the return value.
+ def paramcheck(param)
+ param = param.to_s
+ # Now make sure it's a valid argument to our class. These checks
+ # are organized in order of commonhood -- most types, it's a valid
+ # argument and paramcheck is enabled.
+ if @ref.typeclass.validattr?(param)
+ true
+ elsif %w{name title}.include?(param) # always allow these
+ true
+ elsif paramcheck?
+ self.fail Puppet::ParseError, "Invalid parameter '%s' for type '%s'" %
+ [param, @ref.type]
+ end
+ end
+
def rails_args
return [:type, :title, :line, :exported].inject({}) do |hash, param|
# 'type' isn't a valid column name, so we have to use another name.
@@ -400,6 +425,26 @@ class Puppet::Parser::Resource
hash
end
end
-end
-# $Id$
+ # Define a parameter in our resource.
+ def set_parameter(param, value = nil)
+ if value
+ param = Puppet::Parser::Resource::Param.new(
+ :name => param, :value => value, :source => self.source
+ )
+ elsif ! param.is_a?(Puppet::Parser::Resource::Param)
+ raise ArgumentError, "Must pass a parameter or all necessary values"
+ end
+
+ # And store it in our parameter hash.
+ @params[param.name] = param
+ end
+
+ # Make sure the resource's parameters are all valid for the type.
+ def validate
+ @params.each do |name, param|
+ # Make sure it's a valid parameter.
+ paramcheck(name)
+ end
+ end
+end
diff --git a/lib/puppet/parser/resource/reference.rb b/lib/puppet/parser/resource/reference.rb
index 19d179660..543ee7195 100644
--- a/lib/puppet/parser/resource/reference.rb
+++ b/lib/puppet/parser/resource/reference.rb
@@ -15,7 +15,7 @@ class Puppet::Parser::Resource::Reference
end
end
- self.builtin
+ @builtin
end
def builtintype
@@ -26,13 +26,32 @@ class Puppet::Parser::Resource::Reference
end
end
- # Return the defined type for our obj.
+ # Return the defined type for our obj. This can return classes,
+ # definitions or nodes.
def definedtype
unless defined? @definedtype
- if tmp = @scope.finddefine(self.type)
+ type = self.type.to_s.downcase
+ name = self.title
+ case type
+ when "class": # look for host classes
+ if self.title == :main
+ tmp = @scope.findclass("")
+ else
+ tmp = @scope.findclass(self.title)
+ end
+ when "node": # look for node definitions
+ tmp = @scope.parser.nodes[self.title]
+ else # normal definitions
+ # We have to swap these variables around so the errors are right.
+ name = type
+ type = "type"
+ tmp = @scope.finddefine(self.type)
+ end
+
+ if tmp
@definedtype = tmp
else
- fail Puppet::ParseError, "Could not find resource type '%s'" % self.type
+ fail Puppet::ParseError, "Could not find resource %s '%s'" % [type, name]
end
end
@@ -50,7 +69,7 @@ class Puppet::Parser::Resource::Reference
def to_s
unless defined? @namestring
- @namestring = "%s[%s]" % [type.capitalize, title]
+ @namestring = "%s[%s]" % [type.split("::").collect { |s| s.capitalize }.join("::"), title]
end
@namestring
end
@@ -67,5 +86,3 @@ class Puppet::Parser::Resource::Reference
@typeclass
end
end
-
-# $Id$
diff --git a/lib/puppet/parser/scope.rb b/lib/puppet/parser/scope.rb
index 1fb4f6906..028414cc0 100644
--- a/lib/puppet/parser/scope.rb
+++ b/lib/puppet/parser/scope.rb
@@ -15,16 +15,17 @@ class Puppet::Parser::Scope
include Enumerable
include Puppet::Util::Errors
- attr_accessor :parent, :level, :interp, :source
- attr_accessor :name, :type, :base, :keyword
- attr_accessor :top, :translated, :exported, :virtual, :configuration
+ attr_accessor :parent, :level, :parser, :source, :resource
+ attr_accessor :base, :keyword, :nodescope
+ attr_accessor :top, :translated, :compile
# Proxy accessors
def host
- @configuration.host
+ @compile.node.name
end
+
def interpreter
- @configuration.interpreter
+ @compile.interpreter
end
# Is the value true? This allows us to control the definition of truth
@@ -47,100 +48,14 @@ class Puppet::Parser::Scope
end
end
- # Is the type a builtin type?
- def builtintype?(type)
- if typeklass = Puppet::Type.type(type)
- return typeklass
- else
- return false
- end
- end
-
- # Create a new child scope.
- def child=(scope)
- @children.push(scope)
-
- # Copy all of the shared tables over to the child.
- @@sharedtables.each do |name|
- scope.send(name.to_s + "=", self.send(name))
- end
- end
-
- # Verify that the given object isn't defined elsewhere.
- def chkobjectclosure(obj)
- if exobj = @definedtable[obj.ref]
- typeklass = Puppet::Type.type(obj.type)
- if typeklass and ! typeklass.isomorphic?
- Puppet.info "Allowing duplicate %s" % type
- else
- # Either it's a defined type, which are never
- # isomorphic, or it's a non-isomorphic type.
- msg = "Duplicate definition: %s is already defined" % obj.ref
-
- if exobj.file and exobj.line
- msg << " in file %s at line %s" %
- [exobj.file, exobj.line]
- end
-
- if obj.line or obj.file
- msg << "; cannot redefine"
- end
-
- raise Puppet::ParseError.new(msg)
- end
- end
-
- return true
- end
-
- # Remove a specific child.
- def delete(child)
- @children.delete(child)
- end
-
- # Remove a resource from the various tables. This is only used when
- # a resource maps to a definition and gets evaluated.
- def deleteresource(resource)
- if @definedtable[resource.ref]
- @definedtable.delete(resource.ref)
- end
-
- if @children.include?(resource)
- @children.delete(resource)
- end
- end
-
# Are we the top scope?
def topscope?
@level == 1
end
- # Yield each child scope in turn
- def each
- @children.each { |child|
- yield child
- }
- end
-
- # Evaluate a list of classes.
- def evalclasses(*classes)
- retval = []
- classes.each do |klass|
- if obj = findclass(klass)
- obj.safeevaluate :scope => self
- retval << klass
- end
- end
- retval
- end
-
- def exported?
- self.exported
- end
-
def findclass(name)
@namespaces.each do |namespace|
- if r = interp.findclass(namespace, name)
+ if r = parser.findclass(namespace, name)
return r
end
end
@@ -149,7 +64,7 @@ class Puppet::Parser::Scope
def finddefine(name)
@namespaces.each do |namespace|
- if r = interp.finddefine(namespace, name)
+ if r = parser.finddefine(namespace, name)
return r
end
end
@@ -157,25 +72,11 @@ class Puppet::Parser::Scope
end
def findresource(string, name = nil)
- if name
- string = "%s[%s]" % [string.capitalize, name]
- end
-
- @definedtable[string]
- end
-
- # Recursively complete the whole tree, in preparation for
- # translation or storage.
- def finish
- self.each do |obj|
- obj.finish
- end
+ compile.findresource(string, name)
end
- # Initialize our new scope. Defaults to having no parent and to
- # being declarative.
+ # Initialize our new scope. Defaults to having no parent.
def initialize(hash = {})
- @finished = false
if hash.include?(:namespace)
if n = hash[:namespace]
@namespaces = [n]
@@ -195,16 +96,13 @@ class Puppet::Parser::Scope
@tags = []
- # Our child scopes and objects
- @children = []
-
# The symbol table for this scope. This is where we store variables.
@symtable = {}
# All of the defaults set for types. It's a hash of hashes,
# with the first key being the type, then the second key being
# the parameter.
- @defaultstable = Hash.new { |dhash,type|
+ @defaults = Hash.new { |dhash,type|
dhash[type] = {}
}
end
@@ -225,8 +123,8 @@ class Puppet::Parser::Scope
# then override them with any current values
# this should probably be done differently
- if @defaultstable.include?(type)
- @defaultstable[type].each { |var,value|
+ if @defaults.include?(type)
+ @defaults[type].each { |var,value|
values[var] = value
}
end
@@ -236,17 +134,6 @@ class Puppet::Parser::Scope
return values
end
- # Look up all of the exported objects of a given type.
- def lookupexported(type)
- @definedtable.find_all do |name, r|
- r.type == type and r.exported?
- end
- end
-
- def lookupoverrides(obj)
- @overridetable[obj.ref]
- end
-
# Look up a defined type.
def lookuptype(name)
finddefine(name) || findclass(name)
@@ -260,7 +147,7 @@ class Puppet::Parser::Scope
unless klass
raise Puppet::ParseError, "Could not find class %s" % klassname
end
- unless kscope = class_scope(klass)
+ unless kscope = compile.class_scope(klass)
raise Puppet::ParseError, "Class %s has not been evaluated so its variables cannot be referenced" % klass.classname
end
return kscope.lookupvar(shortname, usestring)
@@ -295,11 +182,9 @@ class Puppet::Parser::Scope
@namespaces.dup
end
- # Create a new scope.
- def newscope(hash = {})
- hash[:parent] = self
- #debug "Creating new scope, level %s" % [self.level + 1]
- return Puppet::Parser::Scope.new(hash)
+ # Create a new scope and set these options.
+ def newscope(options = {})
+ compile.newscope(self, options)
end
# Is this class for a node? This is used to make sure that
@@ -307,20 +192,14 @@ class Puppet::Parser::Scope
# is required because of how often the names are used throughout
# the system, including on the client.
def nodescope?
- defined?(@nodescope) and @nodescope
- end
-
- # Return the list of remaining overrides.
- def overrides
- #@overridetable.collect { |name, overs| overs }.flatten
- @overridetable.values.flatten
+ self.nodescope
end
# We probably shouldn't cache this value... But it's a lot faster
# than doing lots of queries.
def parent
unless defined?(@parent)
- @parent = configuration.parent(self)
+ @parent = compile.parent(self)
end
@parent
end
@@ -335,74 +214,11 @@ class Puppet::Parser::Scope
end
end
- def resources
- @definedtable.values
- end
-
- # Store the fact that we've evaluated a given class. We use a hash
- # that gets inherited from the top scope down, rather than a global
- # hash. We store the object ID, not class name, so that we
- # can support multiple unrelated classes with the same name.
- def setclass(klass)
- if klass.is_a?(AST::HostClass)
- unless klass.classname
- raise Puppet::DevError, "Got a %s with no fully qualified name" %
- klass.class
- end
- @configuration.class_set(klass.classname, self)
- else
- raise Puppet::DevError, "Invalid class %s" % klass.inspect
- end
- if klass.is_a?(AST::Node)
- @nodescope = true
- end
- nil
- end
-
- # Set all of our facts in the top-level scope.
- def setfacts(facts)
- facts.each { |var, value|
- self.setvar(var, value)
- }
- end
-
- # Add a new object to our object table and the global list, and do any necessary
- # checks.
- def setresource(obj)
- self.chkobjectclosure(obj)
-
- @children << obj
-
- # Mark the resource as virtual or exported, as necessary.
- if self.exported?
- obj.exported = true
- elsif self.virtual?
- obj.virtual = true
- end
-
- # The global table
- @definedtable[obj.ref] = obj
-
- return obj
- end
-
- # Override a parameter in an existing object. If the object does not yet
- # exist, then cache the override in a global table, so it can be flushed
- # at the end.
- def setoverride(resource)
- resource.override = true
- if obj = @definedtable[resource.ref]
- obj.merge(resource)
- else
- @overridetable[resource.ref] << resource
- end
- end
-
# Set defaults for a type. The typename should already be downcased,
# so that the syntax is isolated. We don't do any kind of type-checking
# here; instead we let the resource do it when the defaults are used.
def setdefaults(type, params)
- table = @defaultstable[type]
+ table = @defaults[type]
# if we got a single param, it'll be in its own array
params = [params] unless params.is_a?(Array)
@@ -410,17 +226,8 @@ class Puppet::Parser::Scope
params.each { |param|
#Puppet.debug "Default for %s is %s => %s" %
# [type,ary[0].inspect,ary[1].inspect]
- if @@declarative
- if table.include?(param.name)
- self.fail "Default already defined for %s { %s }" %
- [type,param.name]
- end
- else
- if table.include?(param.name)
- # we should maybe allow this warning to be turned off...
- Puppet.warning "Replacing default for %s { %s }" %
- [type,param.name]
- end
+ if table.include?(param.name)
+ raise Puppet::ParseError.new("Default already defined for %s { %s }; cannot redefine" % [type, param.name], param.line, param.file)
end
table[param.name] = param
}
@@ -428,23 +235,19 @@ class Puppet::Parser::Scope
# Set a variable in the current scope. This will override settings
# in scopes above, but will not allow variables in the current scope
- # to be reassigned if we're declarative (which is the default).
+ # to be reassigned.
def setvar(name,value, file = nil, line = nil)
#Puppet.debug "Setting %s to '%s' at level %s" %
# [name.inspect,value,self.level]
if @symtable.include?(name)
- if @@declarative
- error = Puppet::ParseError.new("Cannot reassign variable %s" % name)
- if file
- error.file = file
- end
- if line
- error.line = line
- end
- raise error
- else
- Puppet.warning "Reassigning %s to %s" % [name,value]
+ error = Puppet::ParseError.new("Cannot reassign variable %s" % name)
+ if file
+ error.file = file
+ end
+ if line
+ error.line = line
end
+ raise error
end
@symtable[name] = value
end
@@ -508,82 +311,16 @@ class Puppet::Parser::Scope
return out
end
- # Add a tag to our current list. These tags will be added to all
- # of the objects contained in this scope.
- def tag(*ary)
- ary.each { |tag|
- if tag.nil? or tag == ""
- puts caller
- Puppet.debug "got told to tag with %s" % tag.inspect
- next
- end
- unless tag =~ /^\w[-\w]*$/
- fail Puppet::ParseError, "Invalid tag %s" % tag.inspect
- end
- tag = tag.to_s
- unless @tags.include?(tag)
- #Puppet.info "Tagging scope %s with %s" % [self.object_id, tag]
- @tags << tag
- end
- }
- end
-
# Return the tags associated with this scope. It's basically
# just our parents' tags, plus our type. We don't cache this value
# because our parent tags might change between calls.
def tags
- tmp = [] + @tags
- unless ! defined? @type or @type.nil? or @type == ""
- tmp << @type.to_s
- end
- if parent
- #info "Looking for tags in %s" % parent.type
- parent.tags.each { |tag|
- if tag.nil? or tag == ""
- Puppet.debug "parent returned tag %s" % tag.inspect
- next
- end
- unless tmp.include?(tag)
- tmp << tag
- end
- }
- end
- return tmp.sort.uniq
+ resource.tags
end
# Used mainly for logging
def to_s
- if self.name
- return "%s[%s]" % [@type, @name]
- else
- return self.type.to_s
- end
- end
-
- # Convert all of our objects as necessary.
- def translate
- ret = @children.collect do |child|
- case child
- when Puppet::Parser::Resource
- child.to_trans
- when self.class
- child.translate
- else
- devfail "Got %s for translation" % child.class
- end
- end.reject { |o| o.nil? }
- bucket = Puppet::TransBucket.new ret
-
- case self.type
- when "": bucket.type = "main"
- when nil: devfail "A Scope with no type"
- else
- bucket.type = @type
- end
- if self.name
- bucket.name = self.name
- end
- return bucket
+ "Scope(%s)" % @resource.to_s
end
# Undefine a variable; only used for testing.
@@ -592,10 +329,4 @@ class Puppet::Parser::Scope
@symtable.delete(var)
end
end
-
- def virtual?
- self.virtual || self.exported?
- end
end
-
-# $Id$
diff --git a/lib/puppet/parser/templatewrapper.rb b/lib/puppet/parser/templatewrapper.rb
index 3b8cc3a3a..f863e31aa 100644
--- a/lib/puppet/parser/templatewrapper.rb
+++ b/lib/puppet/parser/templatewrapper.rb
@@ -7,16 +7,16 @@ class Puppet::Parser::TemplateWrapper
def initialize(scope, file)
@scope = scope
- @file = Puppet::Module::find_template(file)
+ @file = Puppet::Module::find_template(file, @scope.compile.environment)
unless FileTest.exists?(@file)
raise Puppet::ParseError,
"Could not find template %s" % file
end
- # We'll only ever not have an interpreter in testing, but, eh.
- if @scope.interp
- @scope.interp.newfile(@file)
+ # We'll only ever not have a parser in testing, but, eh.
+ if @scope.parser
+ @scope.parser.watch_file(@file)
end
end
diff --git a/lib/puppet/propertychange.rb b/lib/puppet/propertychange.rb
index 964ff2b31..35bbede1a 100644
--- a/lib/puppet/propertychange.rb
+++ b/lib/puppet/propertychange.rb
@@ -139,5 +139,3 @@ module Puppet
end
end
end
-
-# $Id$
diff --git a/lib/puppet/rails/host.rb b/lib/puppet/rails/host.rb
index ca1e10c93..b7ca4c2e4 100644
--- a/lib/puppet/rails/host.rb
+++ b/lib/puppet/rails/host.rb
@@ -28,7 +28,7 @@ class Puppet::Rails::Host < ActiveRecord::Base
end
# Store our host in the database.
- def self.store(hash)
+ def self.store(node, resources)
unless name = hash[:name]
raise ArgumentError, "You must specify the hostname for storage"
end
@@ -40,23 +40,19 @@ class Puppet::Rails::Host < ActiveRecord::Base
#unless host = find_by_name(name)
seconds = Benchmark.realtime {
unless host = find_by_name(name)
- host = new(:name => name)
+ host = new(:name => node.name)
end
}
Puppet.notice("Searched for host in %0.2f seconds" % seconds) if defined?(Puppet::TIME_DEBUG)
- if ip = hash[:facts]["ipaddress"]
+ if ip = node.parameters["ipaddress"]
host.ip = ip
end
# Store the facts into the database.
- host.setfacts(hash[:facts])
-
- unless hash[:resources]
- raise ArgumentError, "You must pass resources"
- end
+ host.setfacts node.parameters
seconds = Benchmark.realtime {
- host.setresources(hash[:resources])
+ host.setresources(resources)
}
Puppet.notice("Handled resources in %0.2f seconds" % seconds) if defined?(Puppet::TIME_DEBUG)
diff --git a/lib/puppet/rails/resource.rb b/lib/puppet/rails/resource.rb
index 19aeb9205..785c63419 100644
--- a/lib/puppet/rails/resource.rb
+++ b/lib/puppet/rails/resource.rb
@@ -104,16 +104,16 @@ class Puppet::Rails::Resource < ActiveRecord::Base
end
hash[:scope] = scope
hash[:source] = scope.source
- obj = Puppet::Parser::Resource.new(hash)
-
+ hash[:params] = []
names = []
self.param_names.each do |pname|
# We can get the same name multiple times because of how the
# db layout works.
next if names.include?(pname.name)
names << pname.name
- obj.set(pname.to_resourceparam(self, scope.source))
+ hash[:params] << pname.to_resourceparam(self, scope.source)
end
+ obj = Puppet::Parser::Resource.new(hash)
# Store the ID, so we can check if we're re-collecting the same resource.
obj.rails_id = self.id
@@ -121,5 +121,3 @@ class Puppet::Rails::Resource < ActiveRecord::Base
return obj
end
end
-
-# $Id$
diff --git a/lib/puppet/sslcertificates/ca.rb b/lib/puppet/sslcertificates/ca.rb
index 018640d36..5e355f7fe 100644
--- a/lib/puppet/sslcertificates/ca.rb
+++ b/lib/puppet/sslcertificates/ca.rb
@@ -1,3 +1,5 @@
+require 'sync'
+
class Puppet::SSLCertificates::CA
include Puppet::Util::Warnings
@@ -226,6 +228,33 @@ class Puppet::SSLCertificates::CA
}
end
+ # Create an exclusive lock for reading and writing, and do the
+ # writing in a tmp file.
+ def readwritelock(file, mode = 0600)
+ tmpfile = file + ".tmp"
+ sync = Sync.new
+ unless FileTest.directory?(File.dirname(tmpfile))
+ raise Puppet::DevError, "Cannot create %s; directory %s does not exist" %
+ [file, File.dirname(file)]
+ end
+ sync.synchronize(Sync::EX) do
+ File.open(file, "r+", mode) do |rf|
+ rf.lock_exclusive do
+ File.open(tmpfile, "w", mode) do |tf|
+ yield tf
+ end
+ begin
+ File.rename(tmpfile, file)
+ rescue => detail
+ Puppet.err "Could not rename %s to %s: %s" %
+ [file, tmpfile, detail]
+ end
+ end
+ end
+ end
+ end
+
+
# Sign a given certificate request.
def sign(csr)
unless csr.is_a?(OpenSSL::X509::Request)
@@ -238,7 +267,14 @@ class Puppet::SSLCertificates::CA
raise Puppet::Error, "CSR sign verification failed"
end
- serial = File.read(@config[:serial]).chomp.hex
+ serial = nil
+ readwritelock(@config[:serial]) { |f|
+ serial = File.read(@config[:serial]).chomp.hex
+
+ # increment the serial
+ f << "%04X" % (serial + 1)
+ }
+
newcert = Puppet::SSLCertificates.mkcert(
:type => :server,
:name => csr.subject,
@@ -248,10 +284,6 @@ class Puppet::SSLCertificates::CA
:publickey => csr.public_key
)
- # increment the serial
- Puppet.config.write(:serial) do |f|
- f << "%04X" % (serial + 1)
- end
sign_with_key(newcert)
diff --git a/lib/puppet/type/component.rb b/lib/puppet/type/component.rb
index 6366c7f24..5905d85ab 100644
--- a/lib/puppet/type/component.rb
+++ b/lib/puppet/type/component.rb
@@ -152,19 +152,21 @@ Puppet::Type.newtype(:component) do
# Component paths are special because they function as containers.
def pathbuilder
tmp = []
- if defined? @parent and @parent
- tmp += [@parent.pathbuilder, self.title]
- else
- # The top-level name is always main[top], so we don't bother with
- # that.
- if self.title == "main[top]"
- tmp << "" # This empty field results in "//" in the path
- else
- tmp << self.title
+ myname = ""
+ if self.title =~ /^class\[(.+)\]$/
+ # 'main' is the top class, so we want to see '//' instead of
+ # its name.
+ unless $1 == "main"
+ myname = $1
end
+ else
+ myname = self.title
+ end
+ if self.parent
+ return [@parent.pathbuilder, myname]
+ else
+ return [myname]
end
-
- tmp
end
# Remove an object. The argument determines whether the object's
diff --git a/lib/puppet/type/pfile.rb b/lib/puppet/type/pfile.rb
index 5eb81c3e6..99b5a7435 100644
--- a/lib/puppet/type/pfile.rb
+++ b/lib/puppet/type/pfile.rb
@@ -4,6 +4,7 @@ require 'etc'
require 'uri'
require 'fileutils'
require 'puppet/network/handler'
+require 'puppet/util/diff'
module Puppet
newtype(:file) do
@@ -1047,7 +1048,7 @@ module Puppet
# Write out the file. We open the file correctly, with all of the
# uid and mode and such, and then yield the file handle for actual
# writing.
- def write(usetmp = true)
+ def write(property, usetmp = true)
mode = self.should(:mode)
remove_existing(:file)
diff --git a/lib/puppet/type/pfile/content.rb b/lib/puppet/type/pfile/content.rb
index 7892ed522..11458ef18 100755
--- a/lib/puppet/type/pfile/content.rb
+++ b/lib/puppet/type/pfile/content.rb
@@ -1,5 +1,7 @@
module Puppet
Puppet.type(:file).newproperty(:content) do
+ include Puppet::Util::Diff
+
desc "Specify the contents of a file as a string. Newlines, tabs, and
spaces can be specified using the escaped syntax (e.g., \\n for a
newline). The primary purpose of this parameter is to provide a
@@ -30,6 +32,15 @@ module Puppet
end
end
+ # Override this method to provide diffs if asked for.
+ def insync?(is)
+ result = super
+ if ! result and Puppet[:show_diff] and File.exists?(@resource[:path])
+ string_file_diff(@resource[:path], self.should)
+ end
+ return result
+ end
+
# We should probably take advantage of existing md5 sums if they're there,
# but I really don't feel like dealing with the complexity right now.
def retrieve
@@ -62,11 +73,9 @@ module Puppet
def sync
return_event = @resource.stat ? :file_changed : :file_created
- @resource.write { |f| f.print self.should }
+ @resource.write(:content) { |f| f.print self.should }
return return_event
end
end
end
-
-# $Id$
diff --git a/lib/puppet/type/pfile/source.rb b/lib/puppet/type/pfile/source.rb
index b4c96b3da..1849d5a61 100755
--- a/lib/puppet/type/pfile/source.rb
+++ b/lib/puppet/type/pfile/source.rb
@@ -5,6 +5,7 @@ module Puppet
# this state, during retrieval, modifies the appropriate other states
# so that things get taken care of appropriately.
Puppet.type(:file).newproperty(:source) do
+ include Puppet::Util::Diff
attr_accessor :source, :local
desc "Copy a file over the current file. Uses ``checksum`` to
@@ -158,7 +159,17 @@ module Puppet
end
# Now, we just check to see if the checksums are the same
parentchecksum = @resource.property(:checksum).retrieve
- return (!parentchecksum.nil? and (parentchecksum == @stats[:checksum]))
+ result = (!parentchecksum.nil? and (parentchecksum == @stats[:checksum]))
+
+ # Diff the contents if they ask it. This is quite annoying -- we need to do this in
+ # 'insync?' because they might be in noop mode, but we don't want to do the file
+ # retrieval twice, so we cache the value annoyingly.
+ if ! result and Puppet[:show_diff] and File.exists?(@resource[:path]) and ! @stats[:_diffed]
+ @stats[:_remote_content] = get_remote_content
+ string_file_diff(@resource[:path], @stats[:_remote_content])
+ @stats[:_diffed] = true
+ end
+ return result
end
def pinparams
@@ -236,6 +247,21 @@ module Puppet
end
def sync
+ contents = @stats[:_remote_content] || get_remote_content()
+
+ exists = File.exists?(@resource[:path])
+
+ @resource.write(:source) { |f| f.print contents }
+
+ if exists
+ return :file_changed
+ else
+ return :file_created
+ end
+ end
+
+ private
+ def get_remote_content
unless @stats[:type] == "file"
#if @stats[:type] == "directory"
#[@resource.name, @should.inspect]
@@ -264,17 +290,8 @@ module Puppet
self.notice "Could not retrieve contents for %s" %
@source
end
- exists = File.exists?(@resource[:path])
- @resource.write { |f| f.print contents }
-
- if exists
- return :file_changed
- else
- return :file_created
- end
+ return contents
end
end
end
-
-# $Id$
diff --git a/lib/puppet/util.rb b/lib/puppet/util.rb
index d1d14977c..5a10f5344 100644
--- a/lib/puppet/util.rb
+++ b/lib/puppet/util.rb
@@ -201,7 +201,7 @@ module Util
raise Puppet::DevError, "Failed to provide level to :benchmark"
end
- unless object.respond_to? level
+ unless level == :none or object.respond_to? level
raise Puppet::DevError, "Benchmarked object does not respond to %s" % level
end
diff --git a/lib/puppet/util/autoload.rb b/lib/puppet/util/autoload.rb
index be9e14671..280961837 100644
--- a/lib/puppet/util/autoload.rb
+++ b/lib/puppet/util/autoload.rb
@@ -3,7 +3,7 @@ class Puppet::Util::Autoload
include Puppet::Util
@autoloaders = {}
- @loaded = {}
+ @loaded = []
class << self
attr_reader :autoloaders
@@ -13,12 +13,6 @@ class Puppet::Util::Autoload
# Send [], []=, and :clear to the @autloaders hash
Puppet::Util.classproxy self, :autoloaders, "[]", "[]="
- # Clear the list of autoloaders and loaded files.
- def self.clear
- @autoloaders.clear
- @loaded.clear
- end
-
# List all loaded files.
def self.list_loaded
@loaded.sort { |a,b| a[0] <=> b[0] }.collect do |path, hash|
@@ -27,15 +21,20 @@ class Puppet::Util::Autoload
end
# Has a given path been loaded? This is used for testing whether a
- # changed file should be loaded or just ignored.
+ # changed file should be loaded or just ignored. This is only
+ # used in network/client/master, when downloading plugins, to
+ # see if a given plugin is currently loaded and thus should be
+ # reloaded.
def self.loaded?(path)
path = path.to_s.sub(/\.rb$/, '')
- @loaded[path]
+ @loaded.include?(path)
end
- # Save the fact that a given path has been loaded
- def self.loaded(path, file, loader)
- @loaded[path] = {:file => file, :autoloader => loader}
+ # Save the fact that a given path has been loaded. This is so
+ # we can load downloaded plugins if they've already been loaded
+ # into memory.
+ def self.loaded(file)
+ @loaded << file unless @loaded.include?(file)
end
attr_accessor :object, :path, :objwarn, :wrap
@@ -94,7 +93,7 @@ class Puppet::Util::Autoload
# Mark the named object as loaded. Note that this supports unqualified
# queries, while we store the result as a qualified query in the class.
def loaded(name, file)
- self.class.loaded(File.join(@path, name.to_s), file, object)
+ self.class.loaded(File.join(@path, name.to_s))
end
# Indicate whether the specfied plugin has been loaded.
@@ -150,5 +149,3 @@ class Puppet::Util::Autoload
[module_lib_dirs, Puppet[:libdir], $:].flatten
end
end
-
-# $Id$
diff --git a/lib/puppet/util/config.rb b/lib/puppet/util/config.rb
index 932314215..9cdb4cfe3 100644
--- a/lib/puppet/util/config.rb
+++ b/lib/puppet/util/config.rb
@@ -11,45 +11,34 @@ class Puppet::Util::Config
@@sync = Sync.new
- attr_reader :file, :timer
+ attr_accessor :file
+ attr_reader :timer
# Retrieve a config value
def [](param)
- param = symbolize(param)
-
- # Yay, recursion.
- self.reparse() unless param == :filetimeout
-
- # Cache the returned values; this method was taking close to
- # 10% of the compile time.
- unless @returned[param]
- if @config.include?(param)
- if @config[param]
- @returned[param] = @config[param].value
- end
- else
- raise ArgumentError, "Undefined configuration parameter '%s'" % param
- end
- end
-
- return @returned[param]
+ value(param)
end
# Set a config value. This doesn't set the defaults, it sets the value itself.
def []=(param, value)
@@sync.synchronize do # yay, thread-safe
param = symbolize(param)
- unless @config.include?(param)
- raise Puppet::Error,
+ unless element = @config[param]
+ raise ArgumentError,
"Attempt to assign a value to unknown configuration parameter %s" % param.inspect
end
- unless @order.include?(param)
- @order << param
+ if element.respond_to?(:munge)
+ value = element.munge(value)
+ end
+ if element.respond_to?(:handle)
+ element.handle(value)
end
- @config[param].value = value
- if @returned.include?(param)
- @returned.delete(param)
+ # Reset the name, so it's looked up again.
+ if param == :name
+ @name = nil
end
+ @values[:memory][param] = value
+ @cache.clear
end
return value
@@ -58,7 +47,7 @@ class Puppet::Util::Config
# A simplified equality operator.
def ==(other)
self.each { |myname, myobj|
- unless other[myname] == myobj.value
+ unless other[myname] == value(myname)
return false
end
}
@@ -113,45 +102,69 @@ class Puppet::Util::Config
obj.clear
end
}
- @returned.clear
+ @values.each do |name, values|
+ next if name == :cli and exceptcli
+ @values.delete(name)
+ end
# Don't clear the 'used' in this case, since it's a config file reparse,
# and we want to retain this info.
unless exceptcli
@used = []
end
+
+ @cache.clear
+
+ @name = nil
end
# This is mostly just used for testing.
def clearused
- @returned.clear
+ @cache.clear
@used = []
end
- def each
- @order.each { |name|
- if @config.include?(name)
- yield name, @config[name]
+ # Do variable interpolation on the value.
+ def convert(value)
+ return value unless value
+ return value unless value.is_a? String
+ newval = value.gsub(/\$(\w+)|\$\{(\w+)\}/) do |value|
+ varname = $2 || $1
+ if pval = self.value(varname)
+ pval
else
- raise Puppet::DevError, "%s is in the order but does not exist" % name
+ raise Puppet::DevError, "Could not find value for %s" % parent
end
+ end
+
+ return newval
+ end
+
+ # Return a value's description.
+ def description(name)
+ if obj = @config[symbolize(name)]
+ obj.desc
+ else
+ nil
+ end
+ end
+
+ def each
+ @config.each { |name, object|
+ yield name, object
}
end
# Iterate over each section name.
def eachsection
yielded = []
- @order.each { |name|
- if @config.include?(name)
- section = @config[name].section
- unless yielded.include? section
- yield section
- yielded << section
- end
- else
- raise Puppet::DevError, "%s is in the order but does not exist" % name
+ @config.each do |name, object|
+ section = object.section
+ unless yielded.include? section
+ yield section
+ yielded << section
end
- }
+ end
end
# Return an object by name.
@@ -162,6 +175,7 @@ class Puppet::Util::Config
# Handle a command-line argument.
def handlearg(opt, value = nil)
+ @cache.clear
value = munge_value(value) if value
str = opt.sub(/^--/,'')
bool = true
@@ -170,16 +184,13 @@ class Puppet::Util::Config
str = newstr
bool = false
end
+ str = str.intern
if self.valid?(str)
if self.boolean?(str)
- self[str] = bool
+ @values[:cli][str] = bool
else
- self[str] = value
+ @values[:cli][str] = value
end
-
- # Mark that this was set on the cli, so it's not overridden if the
- # config gets reread.
- @config[str.intern].setbycli = true
else
raise ArgumentError, "Invalid argument %s" % opt
end
@@ -198,12 +209,34 @@ class Puppet::Util::Config
# Create a new config object
def initialize
- @order = []
@config = {}
@shortnames = {}
@created = []
- @returned = {}
+ @searchpath = nil
+
+ # Keep track of set values.
+ @values = Hash.new { |hash, key| hash[key] = {} }
+
+ # And keep a per-environment cache
+ @cache = Hash.new { |hash, key| hash[key] = {} }
+
+ # A central concept of a name.
+ @name = nil
+ end
+
+ # Return a given object's file metadata.
+ def metadata(param)
+ if obj = @config[symbolize(param)] and obj.is_a?(CFile)
+ return [:owner, :group, :mode].inject({}) do |meta, p|
+ if v = obj.send(p)
+ meta[p] = v
+ end
+ meta
+ end
+ else
+ nil
+ end
end
# Make a directory with the appropriate user, group, and mode
@@ -223,28 +256,51 @@ class Puppet::Util::Config
end
end
+ # Figure out our name.
+ def name
+ unless @name
+ unless @config[:name]
+ return nil
+ end
+ searchpath.each do |source|
+ next if source == :name
+ break if @name = @values[source][:name]
+ end
+ unless @name
+ @name = convert(@config[:name].default).intern
+ end
+ end
+ @name
+ end
+
# Return all of the parameters associated with a given section.
- def params(section)
- section = section.intern if section.is_a? String
- @config.find_all { |name, obj|
- obj.section == section
- }.collect { |name, obj|
- name
- }
+ def params(section = nil)
+ if section
+ section = section.intern if section.is_a? String
+ @config.find_all { |name, obj|
+ obj.section == section
+ }.collect { |name, obj|
+ name
+ }
+ else
+ @config.keys
+ end
end
# Parse the configuration file.
def parse(file)
- configmap = parse_file(file)
+ clear(true)
- # We know we want the 'main' section
- if main = configmap[:main]
- set_parameter_hash(main)
+ parse_file(file).each do |area, values|
+ @values[area] = values
end
- # Otherwise, we only want our named section
- if @config.include?(:name) and named = configmap[symbolize(self[:name])]
- set_parameter_hash(named)
+ # We have to do it in the reverse of the search path,
+ # because multiple sections could set the same value.
+ searchpath.reverse.each do |source|
+ if meta = @values[source][:_meta]
+ set_metadata(meta)
+ end
end
end
@@ -309,7 +365,8 @@ class Puppet::Util::Config
# If the parameter is valid, then set it.
if section == Puppet[:name] and @config.include?(var)
- @config[var].value = value
+ #@config[var].value = value
+ @values[:main][var] = value
end
next
end
@@ -318,7 +375,7 @@ class Puppet::Util::Config
# after cli arguments are handled.
unless @config.include?(var) and @config[var].setbycli
Puppet.debug "%s: Setting %s to '%s'" % [section, var, value]
- self[var] = value
+ @values[:main][var] = value
end
@config[var].section = symbolize(section)
@@ -357,11 +414,12 @@ class Puppet::Util::Config
hash[:parent] = self
element = klass.new(hash)
- @order << element.name
-
return element
end
+ # This has to be private, because it doesn't add the elements to @config
+ private :newelement
+
# Iterate across all of the objects in a given section.
def persection(section)
section = symbolize(section)
@@ -393,6 +451,15 @@ class Puppet::Util::Config
end
end
+ # The order in which to search for values.
+ def searchpath(environment = nil)
+ if environment
+ [:cli, :memory, environment, :name, :main]
+ else
+ [:cli, :memory, :name, :main]
+ end
+ end
+
# Get a list of objects per section
def sectionlist
sectionlist = []
@@ -413,7 +480,7 @@ class Puppet::Util::Config
done ||= Hash.new { |hash, key| hash[key] = {} }
objects = []
persection(section) do |obj|
- if @config[:mkusers] and @config[:mkusers].value
+ if @config[:mkusers] and value(:mkusers)
[:owner, :group].each do |attr|
type = nil
if attr == :owner
@@ -461,7 +528,7 @@ class Puppet::Util::Config
end
if obj.respond_to? :to_transportable
- next if obj.value =~ /^\/dev/
+ next if value(obj.name) =~ /^\/dev/
transobjects = obj.to_transportable
transobjects = [transobjects] unless transobjects.is_a? Array
transobjects.each do |trans|
@@ -490,6 +557,9 @@ class Puppet::Util::Config
section = symbolize(section)
defs.each { |name, hash|
if hash.is_a? Array
+ unless hash.length == 2
+ raise ArgumentError, "Defaults specified as an array must contain only the default value and the decription"
+ end
tmp = hash
hash = {}
[:default, :desc].zip(tmp).each { |p,v| hash[p] = v }
@@ -499,7 +569,7 @@ class Puppet::Util::Config
hash[:section] = section
name = hash[:name]
if @config.include?(name)
- raise Puppet::Error, "Parameter %s is already defined" % name
+ raise ArgumentError, "Parameter %s is already defined" % name
end
tryconfig = newelement(hash)
if short = tryconfig.short
@@ -654,6 +724,57 @@ Generated on #{Time.now}.
@config.has_key?(param)
end
+ # Find the correct value using our search path. Optionally accept an environment
+ # in which to search before the other configuration sections.
+ def value(param, environment = nil)
+ param = symbolize(param)
+ environment = symbolize(environment) if environment
+
+ # Short circuit to nil for undefined parameters.
+ return nil unless @config.include?(param)
+
+ # Yay, recursion.
+ self.reparse() unless param == :filetimeout
+
+ # Check the cache first. It needs to be a per-environment
+ # cache so that we don't spread values from one env
+ # to another.
+ if @cache[environment||"none"].include?(param)
+ return @cache[environment||"none"][param]
+ end
+
+ # See if we can find it within our searchable list of values
+ val = nil
+ searchpath(environment).each do |source|
+ # Modify the source as necessary.
+ source = case source
+ when :name:
+ self.name
+ else
+ source
+ end
+
+ # Look for the value. We have to test the hash for whether
+ # it exists, because the value might be false.
+ if @values[source].include?(param)
+ val = @values[source][param]
+ break
+ end
+ end
+
+ # If we didn't get a value, use the default
+ if val.nil?
+ val = @config[param].default
+ end
+
+ # Convert it if necessary
+ val = convert(val)
+
+ # And cache it
+ @cache[environment||"none"][param] = val
+ return val
+ end
+
# Open a file with the appropriate user, group, and mode
def write(default, *args)
obj = nil
@@ -680,7 +801,7 @@ Generated on #{Time.now}.
args << mode
- File.open(obj.value, *args) do |file|
+ File.open(value(obj.name), *args) do |file|
yield file
end
end
@@ -724,30 +845,30 @@ Generated on #{Time.now}.
private
- # Extra extra setting information for files.
+ # Extract extra setting information for files.
def extract_fileinfo(string)
- paramregex = %r{(\w+)\s*=\s*([\w\d]+)}
result = {}
- string.scan(/\{\s*([^}]+)\s*\}/) do
+ value = string.sub(/\{\s*([^}]+)\s*\}/) do
params = $1
params.split(/\s*,\s*/).each do |str|
- if str =~ /^\s*(\w+)\s*=\s*([\w\w]+)\s*$/
+ if str =~ /^\s*(\w+)\s*=\s*([\w\d]+)\s*$/
param, value = $1.intern, $2
result[param] = value
unless [:owner, :mode, :group].include?(param)
- raise Puppet::Error, "Invalid file option '%s'" % param
+ raise ArgumentError, "Invalid file option '%s'" % param
end
if param == :mode and value !~ /^\d+$/
- raise Puppet::Error, "File modes must be numbers"
+ raise ArgumentError, "File modes must be numbers"
end
else
- raise Puppet::Error, "Could not parse '%s'" % string
+ raise ArgumentError, "Could not parse '%s'" % string
end
end
-
- return result
+ ''
end
+ result[:value] = value.sub(/\s*$/, '')
+ return result
return nil
end
@@ -769,26 +890,12 @@ Generated on #{Time.now}.
# support parsing old files with any section, or new files with just two
# valid sections.
def parse_file(file)
- text = nil
-
- if file.is_a? Puppet::Util::LoadedFile
- @file = file
- else
- @file = Puppet::Util::LoadedFile.new(file)
- end
+ text = read_file(file)
# Create a timer so that this file will get checked automatically
# and reparsed if necessary.
settimer()
- begin
- text = File.read(@file.file)
- rescue Errno::ENOENT
- raise Puppet::Error, "No such file %s" % file
- rescue Errno::EACCES
- raise Puppet::Error, "Permission denied to file %s" % file
- end
-
result = Hash.new { |names, name|
names[name] = {}
}
@@ -801,7 +908,7 @@ Generated on #{Time.now}.
text.split(/\n/).each { |line|
count += 1
case line
- when /^\[(\w+)\]$/:
+ when /^\s*\[(\w+)\]$/:
section = $1.intern # Section names
# Add a meta section
result[section][:_meta] ||= {}
@@ -821,6 +928,8 @@ Generated on #{Time.now}.
# Check to see if this is a file argument and it has extra options
begin
if value.is_a?(String) and options = extract_fileinfo(value)
+ value = options[:value]
+ options.delete(:value)
result[section][:_meta][var] = options
end
result[section][var] = value
@@ -840,26 +949,28 @@ Generated on #{Time.now}.
return result
end
- # Take all members of a hash and assign their values appropriately.
- def set_parameter_hash(params)
- params.each do |param, value|
- next if param == :_meta
- unless @config.include?(param)
- Puppet.warning "Discarded unknown configuration parameter %s" % param
- next
- end
- if @config[param].setbycli
- Puppet.debug "Ignoring %s set by config file; overridden by cli" % param
- else
- self[param] = value
- end
+ # Read the file in.
+ def read_file(file)
+ if file.is_a? Puppet::Util::LoadedFile
+ @file = file
+ else
+ @file = Puppet::Util::LoadedFile.new(file)
end
- if meta = params[:_meta]
- meta.each do |var, values|
- values.each do |param, value|
- @config[var].send(param.to_s + "=", value)
- end
+ begin
+ return File.read(@file.file)
+ rescue Errno::ENOENT
+ raise ArgumentError, "No such file %s" % file
+ rescue Errno::EACCES
+ raise ArgumentError, "Permission denied to file %s" % file
+ end
+ end
+
+ # Set file metadata.
+ def set_metadata(meta)
+ meta.each do |var, values|
+ values.each do |param, value|
+ @config[var].send(param.to_s + "=", value)
end
end
end
@@ -874,22 +985,6 @@ Generated on #{Time.now}.
@value = nil
end
- # Do variable interpolation on the value.
- def convert(value)
- return value unless value
- return value unless value.is_a? String
- newval = value.gsub(/\$(\w+)|\$\{(\w+)\}/) do |value|
- varname = $2 || $1
- if pval = @parent[varname]
- pval
- else
- raise Puppet::DevError, "Could not find value for %s" % parent
- end
- end
-
- return newval
- end
-
def desc=(value)
@desc = value.gsub(/^\s*/, '')
end
@@ -964,13 +1059,13 @@ Generated on #{Time.now}.
str += "# The default value is '%s'.\n" % @default
end
- line = "%s = %s" % [@name, self.value]
-
# If the value has not been overridden, then print it out commented
# and unconverted, so it's clear that that's the default and how it
# works.
- if defined? @value and ! @value.nil?
- line = "%s = %s" % [@name, self.value]
+ value = @parent.value(self.name)
+
+ if value != @default
+ line = "%s = %s" % [@name, value]
else
line = "# %s = %s" % [@name, @default]
end
@@ -982,37 +1077,7 @@ Generated on #{Time.now}.
# Retrieves the value, or if it's not set, retrieves the default.
def value
- retval = nil
- if defined? @value and ! @value.nil?
- retval = @value
- elsif defined? @default
- retval = @default
- else
- return nil
- end
-
- if retval.is_a? String
- return convert(retval)
- else
- return retval
- end
- end
-
- # Set the value.
- def value=(value)
- if respond_to?(:validate)
- validate(value)
- end
-
- if respond_to?(:munge)
- @value = munge(value)
- else
- @value = value
- end
-
- if respond_to?(:handle)
- handle(@value)
- end
+ @parent.value(self.name)
end
end
@@ -1023,7 +1088,7 @@ Generated on #{Time.now}.
def group
if defined? @group
- return convert(@group)
+ return @parent.convert(@group)
else
return nil
end
@@ -1031,7 +1096,7 @@ Generated on #{Time.now}.
def owner
if defined? @owner
- return convert(@owner)
+ return @parent.convert(@owner)
else
return nil
end
@@ -1049,7 +1114,7 @@ Generated on #{Time.now}.
# Return the appropriate type.
def type
- value = self.value
+ value = @parent.value(self.name)
if @name.to_s =~ /dir/
return :directory
elsif value.to_s =~ /\/$/
@@ -1068,11 +1133,15 @@ Generated on #{Time.now}.
def to_transportable
type = self.type
return nil unless type
- path = self.value.split(File::SEPARATOR)
+ path = @parent.value(self.name).split(File::SEPARATOR)
path.shift # remove the leading nil
objects = []
- obj = Puppet::TransObject.new(self.value, "file")
+ path = self.value
+ unless path =~ /^#{File::SEPARATOR}/
+ path = File.join(Dir.getwd, path)
+ end
+ obj = Puppet::TransObject.new(path, "file")
# Only create directories, or files that are specifically marked to
# create.
@@ -1081,7 +1150,7 @@ Generated on #{Time.now}.
end
[:mode].each { |var|
if value = self.send(var)
- # Don't both converting the mode, since the file type
+ # Don't bother converting the mode, since the file type
# can handle it any old way.
obj[var] = value
end
@@ -1143,7 +1212,7 @@ Generated on #{Time.now}.
when true, "true": return true
when false, "false": return false
else
- raise Puppet::Error, "Invalid value '%s' for %s" %
+ raise ArgumentError, "Invalid value '%s' for %s" %
[value.inspect, @name]
end
end
diff --git a/lib/puppet/util/diff.rb b/lib/puppet/util/diff.rb
new file mode 100644
index 000000000..e6ff57108
--- /dev/null
+++ b/lib/puppet/util/diff.rb
@@ -0,0 +1,71 @@
+# Provide a diff between two strings.
+module Puppet::Util::Diff
+ include Puppet::Util
+ require 'tempfile'
+
+ def diff(old, new)
+ command = [Puppet[:diff]]
+ if args = Puppet[:diff_args] and args != ""
+ command << args
+ end
+ command << old << new
+ execute(command, :failonfail => false)
+ end
+
+ module_function :diff
+
+ # return diff string of two input strings
+ # format defaults to unified
+ # context defaults to 3 lines
+ def lcs_diff(data_old, data_new, format=:unified, context_lines=3)
+ unless Puppet.features.diff?
+ Puppet.warning "Cannot provide diff without the diff/lcs Ruby library"
+ return ""
+ end
+ data_old = data_old.split(/\n/).map! { |e| e.chomp }
+ data_new = data_new.split(/\n/).map! { |e| e.chomp }
+
+ output = ""
+
+ diffs = Diff::LCS.diff(data_old, data_new)
+ return output if diffs.empty?
+
+ oldhunk = hunk = nil
+ file_length_difference = 0
+
+ diffs.each do |piece|
+ begin
+ hunk = Diff::LCS::Hunk.new(data_old, data_new, piece,
+ context_lines,
+ file_length_difference)
+ file_length_difference = hunk.file_length_difference
+ next unless oldhunk
+ # Hunks may overlap, which is why we need to be careful when our
+ # diff includes lines of context. Otherwise, we might print
+ # redundant lines.
+ if (context_lines > 0) and hunk.overlaps?(oldhunk)
+ hunk.unshift(oldhunk)
+ else
+ output << oldhunk.diff(format)
+ end
+ ensure
+ oldhunk = hunk
+ output << "\n"
+ end
+ end
+
+ # Handle the last remaining hunk
+ output << oldhunk.diff(format) << "\n"
+ end
+
+ def string_file_diff(path, string)
+ require 'tempfile'
+ tempfile = Tempfile.new("puppet-diffing")
+ tempfile.open
+ tempfile.print string
+ tempfile.close
+ print diff(path, tempfile.path)
+ tempfile.delete
+ end
+end
+
diff --git a/lib/puppet/util/methodhelper.rb b/lib/puppet/util/methodhelper.rb
index 63158ab67..32fca1877 100644
--- a/lib/puppet/util/methodhelper.rb
+++ b/lib/puppet/util/methodhelper.rb
@@ -33,5 +33,3 @@ module Puppet::Util::MethodHelper
end
end
end
-
-# $Id$
diff --git a/spec/Rakefile b/spec/Rakefile
new file mode 100644
index 000000000..40d107312
--- /dev/null
+++ b/spec/Rakefile
@@ -0,0 +1,10 @@
+require File.join(File.dirname(__FILE__), "spec_helper.rb")
+require 'rake'
+require 'spec/rake/spectask'
+
+desc "Run all spec unit tests"
+Spec::Rake::SpecTask.new('unit') do |t|
+ t.spec_files = FileList['unit/**/*.rb']
+end
+
+task :default => [:unit]
diff --git a/spec/bin/spec b/spec/bin/spec
new file mode 100755
index 000000000..a7e6ce0cb
--- /dev/null
+++ b/spec/bin/spec
@@ -0,0 +1,3 @@
+$LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__) + "/../lib"))
+require 'spec'
+::Spec::Runner::CommandLine.run(ARGV, STDERR, STDOUT, true, true)
diff --git a/test/lib/spec.rb b/spec/lib/spec.rb
index 9a83c1d5e..48c12595c 100644
--- a/test/lib/spec.rb
+++ b/spec/lib/spec.rb
@@ -1,8 +1,13 @@
-require 'spec/deprecated'
+require 'spec/extensions'
require 'spec/version'
-require 'spec/callback'
require 'spec/matchers'
require 'spec/expectations'
-require 'spec/mocks'
-require 'spec/runner'
require 'spec/translator'
+require 'spec/dsl'
+require 'spec/runner'
+
+class Object
+ def metaclass
+ class << self; self; end
+ end
+end
diff --git a/spec/lib/spec/dsl.rb b/spec/lib/spec/dsl.rb
new file mode 100644
index 000000000..f960eb907
--- /dev/null
+++ b/spec/lib/spec/dsl.rb
@@ -0,0 +1,11 @@
+require 'spec/dsl/description'
+require 'spec/dsl/errors'
+require 'spec/dsl/configuration'
+require 'spec/dsl/behaviour_callbacks'
+require 'spec/dsl/behaviour'
+require 'spec/dsl/behaviour_eval'
+require 'spec/dsl/composite_proc_builder'
+require 'spec/dsl/example'
+require 'spec/dsl/example_matcher'
+require 'spec/dsl/example_should_raise_handler'
+require 'spec/dsl/behaviour_factory'
diff --git a/spec/lib/spec/dsl/behaviour.rb b/spec/lib/spec/dsl/behaviour.rb
new file mode 100644
index 000000000..5158bb673
--- /dev/null
+++ b/spec/lib/spec/dsl/behaviour.rb
@@ -0,0 +1,220 @@
+module Spec
+ module DSL
+ class EvalModule < Module; end
+ class Behaviour
+ extend BehaviourCallbacks
+
+ class << self
+ def add_shared_behaviour(behaviour)
+ return if behaviour.equal?(found_behaviour = find_shared_behaviour(behaviour.description))
+ return if found_behaviour and File.expand_path(behaviour.description[:spec_path]) == File.expand_path(found_behaviour.description[:spec_path])
+ raise ArgumentError.new("Shared Behaviour '#{behaviour.description}' already exists") if found_behaviour
+ shared_behaviours << behaviour
+ end
+
+ def find_shared_behaviour(behaviour_description)
+ shared_behaviours.find { |b| b.description == behaviour_description }
+ end
+
+ def shared_behaviours
+ # TODO - this needs to be global, or at least accessible from
+ # from subclasses of Behaviour in a centralized place. I'm not loving
+ # this as a solution, but it works for now.
+ $shared_behaviours ||= []
+ end
+ end
+
+ def initialize(*args, &behaviour_block)
+ init_description(*args)
+ init_eval_module
+ before_eval
+ eval_behaviour(&behaviour_block)
+ end
+
+ private
+
+ def init_description(*args)
+ unless self.class == Behaviour
+ args << {} unless Hash === args.last
+ args.last[:behaviour_class] = self.class
+ end
+ @description = Description.new(*args)
+ end
+
+ def init_eval_module
+ @eval_module = EvalModule.new
+ @eval_module.extend BehaviourEval::ModuleMethods
+ @eval_module.include BehaviourEval::InstanceMethods
+ @eval_module.include described_type if described_type.class == Module
+ @eval_module.behaviour = self
+ @eval_module.description = @description
+ end
+
+ def eval_behaviour(&behaviour_block)
+ @eval_module.class_eval(&behaviour_block)
+ end
+
+ protected
+
+ def before_eval
+ end
+
+ public
+
+ def run(reporter, dry_run=false, reverse=false, timeout=nil)
+ raise "shared behaviours should never run" if shared?
+ # TODO - change add_behaviour to add_description ??????
+ reporter.add_behaviour(@description)
+ prepare_execution_context_class
+ before_all_errors = run_before_all(reporter, dry_run)
+
+ exs = reverse ? examples.reverse : examples
+ example_execution_context = nil
+
+ if before_all_errors.empty?
+ exs.each do |example|
+ example_execution_context = execution_context(example)
+ example_execution_context.copy_instance_variables_from(@before_and_after_all_context_instance) unless before_all_proc(behaviour_type).nil?
+
+ befores = before_each_proc(behaviour_type) {|e| raise e}
+ afters = after_each_proc(behaviour_type)
+ example.run(reporter, befores, afters, dry_run, example_execution_context, timeout)
+ end
+ end
+
+ @before_and_after_all_context_instance.copy_instance_variables_from(example_execution_context) unless after_all_proc(behaviour_type).nil?
+ run_after_all(reporter, dry_run)
+ end
+
+ def number_of_examples
+ examples.length
+ end
+
+ def matches?(specified_examples)
+ matcher ||= ExampleMatcher.new(description)
+
+ examples.each do |example|
+ return true if example.matches?(matcher, specified_examples)
+ end
+ return false
+ end
+
+ def shared?
+ @description[:shared]
+ end
+
+ def retain_examples_matching!(specified_examples)
+ return if specified_examples.index(description)
+ matcher = ExampleMatcher.new(description)
+ examples.reject! do |example|
+ !example.matches?(matcher, specified_examples)
+ end
+ end
+
+ def methods
+ my_methods = super
+ my_methods |= @eval_module.methods
+ my_methods
+ end
+
+ # Includes modules in the Behaviour (the <tt>describe</tt> block).
+ def include(*args)
+ @eval_module.include(*args)
+ end
+
+ def behaviour_type #:nodoc:
+ @description[:behaviour_type]
+ end
+
+ # Sets the #number on each Example and returns the next number
+ def set_sequence_numbers(number, reverse) #:nodoc:
+ exs = reverse ? examples.reverse : examples
+ exs.each do |example|
+ example.number = number
+ number += 1
+ end
+ number
+ end
+
+ protected
+
+ # Messages that this class does not understand
+ # are passed directly to the @eval_module.
+ def method_missing(sym, *args, &block)
+ @eval_module.send(sym, *args, &block)
+ end
+
+ def prepare_execution_context_class
+ plugin_mock_framework
+ weave_in_included_modules
+ define_predicate_matchers #this is in behaviour_eval
+ execution_context_class
+ end
+
+ def weave_in_included_modules
+ mods = [@eval_module]
+ mods << included_modules.dup
+ mods << Spec::Runner.configuration.modules_for(behaviour_type)
+ execution_context_class.class_eval do
+ # WARNING - the following can be executed in the context of any
+ # class, and should never pass more than one module to include
+ # even though we redefine include in this class. This is NOT
+ # tested anywhere, hence this comment.
+ mods.flatten.each {|mod| include mod}
+ end
+ end
+
+ def execution_context(example)
+ execution_context_class.new(example)
+ end
+
+ def run_before_all(reporter, dry_run)
+ errors = []
+ unless dry_run
+ begin
+ @before_and_after_all_context_instance = execution_context(nil)
+ @before_and_after_all_context_instance.instance_eval(&before_all_proc(behaviour_type))
+ rescue Exception => e
+ errors << e
+ location = "before(:all)"
+ # The easiest is to report this as an example failure. We don't have an Example
+ # at this point, so we'll just create a placeholder.
+ reporter.example_finished(Example.new(location), e, location) if reporter
+ end
+ end
+ errors
+ end
+
+ def run_after_all(reporter, dry_run)
+ unless dry_run
+ begin
+ @before_and_after_all_context_instance ||= execution_context(nil)
+ @before_and_after_all_context_instance.instance_eval(&after_all_proc(behaviour_type))
+ rescue Exception => e
+ location = "after(:all)"
+ reporter.example_finished(Example.new(location), e, location) if reporter
+ end
+ end
+ end
+
+ def plugin_mock_framework
+ case mock_framework = Spec::Runner.configuration.mock_framework
+ when Module
+ include mock_framework
+ else
+ require Spec::Runner.configuration.mock_framework
+ include Spec::Plugins::MockFramework
+ end
+ end
+
+ def description
+ @description.to_s
+ end
+
+ def described_type
+ @description.described_type
+ end
+
+ end
+ end
+end
diff --git a/spec/lib/spec/dsl/behaviour_callbacks.rb b/spec/lib/spec/dsl/behaviour_callbacks.rb
new file mode 100644
index 000000000..8b69ad9e5
--- /dev/null
+++ b/spec/lib/spec/dsl/behaviour_callbacks.rb
@@ -0,0 +1,82 @@
+module Spec
+ module DSL
+ # See http://rspec.rubyforge.org/documentation/before_and_after.html
+ module BehaviourCallbacks
+ def prepend_before(*args, &block)
+ scope, options = scope_and_options(*args)
+ add(scope, options, :before, :unshift, &block)
+ end
+ def append_before(*args, &block)
+ scope, options = scope_and_options(*args)
+ add(scope, options, :before, :<<, &block)
+ end
+ alias_method :before, :append_before
+
+ def prepend_after(*args, &block)
+ scope, options = scope_and_options(*args)
+ add(scope, options, :after, :unshift, &block)
+ end
+ alias_method :after, :prepend_after
+ def append_after(*args, &block)
+ scope, options = scope_and_options(*args)
+ add(scope, options, :after, :<<, &block)
+ end
+
+ def scope_and_options(*args)
+ args, options = args_and_options(*args)
+ scope = (args[0] || :each), options
+ end
+
+ def add(scope, options, where, how, &block)
+ scope ||= :each
+ options ||= {}
+ behaviour_type = options[:behaviour_type]
+ case scope
+ when :each; self.__send__("#{where}_each_parts", behaviour_type).__send__(how, block)
+ when :all; self.__send__("#{where}_all_parts", behaviour_type).__send__(how, block)
+ end
+ end
+
+ def remove_after(scope, &block)
+ after_each_parts.delete(block)
+ end
+
+ # Deprecated. Use before(:each)
+ def setup(&block)
+ before(:each, &block)
+ end
+
+ # Deprecated. Use after(:each)
+ def teardown(&block)
+ after(:each, &block)
+ end
+
+ def before_all_parts(behaviour_type=nil) # :nodoc:
+ @before_all_parts ||= {}
+ @before_all_parts[behaviour_type] ||= []
+ end
+
+ def after_all_parts(behaviour_type=nil) # :nodoc:
+ @after_all_parts ||= {}
+ @after_all_parts[behaviour_type] ||= []
+ end
+
+ def before_each_parts(behaviour_type=nil) # :nodoc:
+ @before_each_parts ||= {}
+ @before_each_parts[behaviour_type] ||= []
+ end
+
+ def after_each_parts(behaviour_type=nil) # :nodoc:
+ @after_each_parts ||= {}
+ @after_each_parts[behaviour_type] ||= []
+ end
+
+ def clear_before_and_after! # :nodoc:
+ @before_all_parts = nil
+ @after_all_parts = nil
+ @before_each_parts = nil
+ @after_each_parts = nil
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/dsl/behaviour_eval.rb b/spec/lib/spec/dsl/behaviour_eval.rb
new file mode 100644
index 000000000..9f7b8281e
--- /dev/null
+++ b/spec/lib/spec/dsl/behaviour_eval.rb
@@ -0,0 +1,231 @@
+module Spec
+ module DSL
+ module BehaviourEval
+ module ModuleMethods
+ include BehaviourCallbacks
+
+ attr_writer :behaviour
+ attr_accessor :description
+
+ # RSpec runs every example in a new instance of Object, mixing in
+ # the behaviour necessary to run examples. Because this behaviour gets
+ # mixed in, it can get mixed in to an instance of any class at all.
+ #
+ # This is something that you would hardly ever use, but there is one
+ # common use case for it - inheriting from Test::Unit::TestCase. RSpec's
+ # Rails plugin uses this feature to provide access to all of the features
+ # that are available for Test::Unit within RSpec examples.
+ def inherit(klass)
+ raise ArgumentError.new("Shared behaviours cannot inherit from classes") if @behaviour.shared?
+ @behaviour_superclass = klass
+ derive_execution_context_class_from_behaviour_superclass
+ end
+
+ # You can pass this one or many modules. Each module will subsequently
+ # be included in the each object in which an example is run. Use this
+ # to provide global helper methods to your examples.
+ #
+ # == Example
+ #
+ # module HelperMethods
+ # def helper_method
+ # ...
+ # end
+ # end
+ #
+ # describe Thing do
+ # include HelperMethods
+ # it "should do stuff" do
+ # helper_method
+ # end
+ # end
+ def include(*mods)
+ mods.each do |mod|
+ included_modules << mod
+ mod.send :included, self
+ end
+ end
+
+ # Use this to pull in examples from shared behaviours.
+ # See Spec::Runner for information about shared behaviours.
+ def it_should_behave_like(behaviour_description)
+ behaviour = @behaviour.class.find_shared_behaviour(behaviour_description)
+ if behaviour.nil?
+ raise RuntimeError.new("Shared Behaviour '#{behaviour_description}' can not be found")
+ end
+ behaviour.copy_to(self)
+ end
+
+ def copy_to(eval_module) # :nodoc:
+ examples.each { |e| eval_module.examples << e; }
+ before_each_parts.each { |p| eval_module.before_each_parts << p }
+ after_each_parts.each { |p| eval_module.after_each_parts << p }
+ before_all_parts.each { |p| eval_module.before_all_parts << p }
+ after_all_parts.each { |p| eval_module.after_all_parts << p }
+ included_modules.each { |m| eval_module.included_modules << m }
+ eval_module.included_modules << self
+ end
+
+ # :call-seq:
+ # predicate_matchers[matcher_name] = method_on_object
+ # predicate_matchers[matcher_name] = [method1_on_object, method2_on_object]
+ #
+ # Dynamically generates a custom matcher that will match
+ # a predicate on your class. RSpec provides a couple of these
+ # out of the box:
+ #
+ # exist (or state expectations)
+ # File.should exist("path/to/file")
+ #
+ # an_instance_of (for mock argument constraints)
+ # mock.should_receive(:message).with(an_instance_of(String))
+ #
+ # == Examples
+ #
+ # class Fish
+ # def can_swim?
+ # true
+ # end
+ # end
+ #
+ # describe Fish do
+ # predicate_matchers[:swim] = :can_swim?
+ # it "should swim" do
+ # Fish.new.should swim
+ # end
+ # end
+ def predicate_matchers
+ @predicate_matchers ||= {:exist => :exist?, :an_instance_of => :is_a?}
+ end
+
+ def define_predicate_matchers(hash=nil) # :nodoc:
+ if hash.nil?
+ define_predicate_matchers(predicate_matchers)
+ define_predicate_matchers(Spec::Runner.configuration.predicate_matchers)
+ else
+ hash.each_pair do |matcher_method, method_on_object|
+ define_method matcher_method do |*args|
+ eval("be_#{method_on_object.to_s.gsub('?','')}(*args)")
+ end
+ end
+ end
+ end
+
+ # Creates an instance of Spec::DSL::Example and adds
+ # it to a collection of examples of the current behaviour.
+ def it(description=:__generate_description, opts={}, &block)
+ examples << Example.new(description, opts, &block)
+ end
+
+ # Alias for it.
+ def specify(description=:__generate_description, opts={}, &block)
+ it(description, opts, &block)
+ end
+
+ def methods # :nodoc:
+ my_methods = super
+ my_methods |= behaviour_superclass.methods
+ my_methods
+ end
+
+ protected
+
+ def method_missing(method_name, *args)
+ if behaviour_superclass.respond_to?(method_name)
+ return execution_context_class.send(method_name, *args)
+ end
+ super
+ end
+
+ def before_each_proc(behaviour_type, &error_handler)
+ parts = []
+ parts.push(*Behaviour.before_each_parts(nil))
+ parts.push(*Behaviour.before_each_parts(behaviour_type)) unless behaviour_type.nil?
+ parts.push(*before_each_parts(nil))
+ parts.push(*before_each_parts(behaviour_type)) unless behaviour_type.nil?
+ CompositeProcBuilder.new(parts).proc(&error_handler)
+ end
+
+ def before_all_proc(behaviour_type, &error_handler)
+ parts = []
+ parts.push(*Behaviour.before_all_parts(nil))
+ parts.push(*Behaviour.before_all_parts(behaviour_type)) unless behaviour_type.nil?
+ parts.push(*before_all_parts(nil))
+ parts.push(*before_all_parts(behaviour_type)) unless behaviour_type.nil?
+ CompositeProcBuilder.new(parts).proc(&error_handler)
+ end
+
+ def after_all_proc(behaviour_type)
+ parts = []
+ parts.push(*after_all_parts(behaviour_type)) unless behaviour_type.nil?
+ parts.push(*after_all_parts(nil))
+ parts.push(*Behaviour.after_all_parts(behaviour_type)) unless behaviour_type.nil?
+ parts.push(*Behaviour.after_all_parts(nil))
+ CompositeProcBuilder.new(parts).proc
+ end
+
+ def after_each_proc(behaviour_type)
+ parts = []
+ parts.push(*after_each_parts(behaviour_type)) unless behaviour_type.nil?
+ parts.push(*after_each_parts(nil))
+ parts.push(*Behaviour.after_each_parts(behaviour_type)) unless behaviour_type.nil?
+ parts.push(*Behaviour.after_each_parts(nil))
+ CompositeProcBuilder.new(parts).proc
+ end
+
+ private
+
+ def execution_context_class
+ @execution_context_class ||= derive_execution_context_class_from_behaviour_superclass
+ end
+
+ def derive_execution_context_class_from_behaviour_superclass
+ @execution_context_class = Class.new(behaviour_superclass)
+ behaviour_superclass.spec_inherited(self) if behaviour_superclass.respond_to?(:spec_inherited)
+ @execution_context_class
+ end
+
+ def behaviour_superclass
+ @behaviour_superclass ||= Object
+ end
+
+ protected
+ def included_modules
+ @included_modules ||= [::Spec::Matchers]
+ end
+
+ def examples
+ @examples ||= []
+ end
+ end
+
+ module InstanceMethods
+ def initialize(*args, &block) #:nodoc:
+ # TODO - inheriting from TestUnit::TestCase fails without this
+ # - let's figure out why and move this somewhere else
+ end
+
+ def violated(message="")
+ raise Spec::Expectations::ExpectationNotMetError.new(message)
+ end
+
+ def inspect
+ "[RSpec example]"
+ end
+
+ def pending(message)
+ if block_given?
+ begin
+ yield
+ rescue Exception => e
+ raise Spec::DSL::ExamplePendingError.new(message)
+ end
+ raise Spec::DSL::PendingFixedError.new("Expected pending '#{message}' to fail. No Error was raised.")
+ else
+ raise Spec::DSL::ExamplePendingError.new(message)
+ end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/dsl/behaviour_factory.rb b/spec/lib/spec/dsl/behaviour_factory.rb
new file mode 100755
index 000000000..44b60c641
--- /dev/null
+++ b/spec/lib/spec/dsl/behaviour_factory.rb
@@ -0,0 +1,42 @@
+module Spec
+ module DSL
+ class BehaviourFactory
+
+ class << self
+
+ BEHAVIOUR_CLASSES = {:default => Spec::DSL::Behaviour}
+
+ # Registers a behaviour class +klass+ with the symbol
+ # +behaviour_type+. For example:
+ #
+ # Spec::DSL::BehaviourFactory.add_behaviour_class(:farm, Spec::Farm::DSL::FarmBehaviour)
+ #
+ # This will cause Kernel#describe from a file living in
+ # <tt>spec/farm</tt> to create behaviour instances of type
+ # Spec::Farm::DSL::FarmBehaviour.
+ def add_behaviour_class(behaviour_type, klass)
+ BEHAVIOUR_CLASSES[behaviour_type] = klass
+ end
+
+ def remove_behaviour_class(behaviour_type)
+ BEHAVIOUR_CLASSES.delete(behaviour_type)
+ end
+
+ def create(*args, &block)
+ opts = Hash === args.last ? args.last : {}
+ if opts[:shared]
+ behaviour_type = :default
+ elsif opts[:behaviour_type]
+ behaviour_type = opts[:behaviour_type]
+ elsif opts[:spec_path] =~ /spec(\\|\/)(#{BEHAVIOUR_CLASSES.keys.join('|')})/
+ behaviour_type = $2.to_sym
+ else
+ behaviour_type = :default
+ end
+ return BEHAVIOUR_CLASSES[behaviour_type].new(*args, &block)
+ end
+
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/dsl/composite_proc_builder.rb b/spec/lib/spec/dsl/composite_proc_builder.rb
new file mode 100644
index 000000000..373f44953
--- /dev/null
+++ b/spec/lib/spec/dsl/composite_proc_builder.rb
@@ -0,0 +1,33 @@
+module Spec
+ module DSL
+ class CompositeProcBuilder < Array
+ def initialize(callbacks=[])
+ push(*callbacks)
+ end
+
+ def proc(&error_handler)
+ parts = self
+ errors = []
+ Proc.new do
+ result = parts.collect do |part|
+ begin
+ if part.is_a?(UnboundMethod)
+ part.bind(self).call
+ else
+ instance_eval(&part)
+ end
+ rescue Exception => e
+ if error_handler
+ error_handler.call(e)
+ else
+ errors << e
+ end
+ end
+ end
+ raise errors.first unless errors.empty?
+ result
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/dsl/configuration.rb b/spec/lib/spec/dsl/configuration.rb
new file mode 100755
index 000000000..709574ded
--- /dev/null
+++ b/spec/lib/spec/dsl/configuration.rb
@@ -0,0 +1,135 @@
+module Spec
+ module DSL
+ class Configuration
+
+ # Chooses what mock framework to use. Example:
+ #
+ # Spec::Runner.configure do |config|
+ # config.mock_with :rspec, :mocha, :flexmock, or :rr
+ # end
+ #
+ # To use any other mock framework, you'll have to provide
+ # your own adapter. This is simply a module that responds to
+ # setup_mocks_for_rspec, verify_mocks_for_rspec and teardown_mocks_for_rspec.
+ # These are your hooks into the lifecycle of a given example. RSpec will
+ # call setup_mocks_for_rspec before running anything else in each Example.
+ # After executing the #after methods, RSpec will then call verify_mocks_for_rspec
+ # and teardown_mocks_for_rspec (this is guaranteed to run even if there are
+ # failures in verify_mocks_for_rspec).
+ #
+ # Once you've defined this module, you can pass that to mock_with:
+ #
+ # Spec::Runner.configure do |config|
+ # config.mock_with MyMockFrameworkAdapter
+ # end
+ #
+ def mock_with(mock_framework)
+ @mock_framework = case mock_framework
+ when Symbol
+ mock_framework_path(mock_framework.to_s)
+ else
+ mock_framework
+ end
+ end
+
+ def mock_framework # :nodoc:
+ @mock_framework ||= mock_framework_path("rspec")
+ end
+
+ # Declares modules to be included in all behaviours (<tt>describe</tt> blocks).
+ #
+ # config.include(My::Bottle, My::Cup)
+ #
+ # If you want to restrict the inclusion to a subset of all the behaviours then
+ # specify this in a Hash as the last argument:
+ #
+ # config.include(My::Pony, My::Horse, :behaviour_type => :farm)
+ #
+ # Only behaviours that have that type will get the modules included:
+ #
+ # describe "Downtown", :behaviour_type => :city do
+ # # Will *not* get My::Pony and My::Horse included
+ # end
+ #
+ # describe "Old Mac Donald", :behaviour_type => :farm do
+ # # *Will* get My::Pony and My::Horse included
+ # end
+ #
+ def include(*args)
+ args << {} unless Hash === args.last
+ modules, options = args_and_options(*args)
+ required_behaviour_type = options[:behaviour_type]
+ required_behaviour_type = required_behaviour_type.to_sym unless required_behaviour_type.nil?
+ @modules ||= {}
+ @modules[required_behaviour_type] ||= []
+ @modules[required_behaviour_type] += modules
+ end
+
+ def modules_for(required_behaviour_type) #:nodoc:
+ @modules ||= {}
+ modules = @modules[nil] || [] # general ones
+ modules << @modules[required_behaviour_type.to_sym] unless required_behaviour_type.nil?
+ modules.uniq.compact
+ end
+
+ # This is just for cleanup in RSpec's own examples
+ def exclude(*modules) #:nodoc:
+ @modules.each do |behaviour_type, mods|
+ modules.each{|m| mods.delete(m)}
+ end
+ end
+
+ # Defines global predicate matchers. Example:
+ #
+ # config.predicate_matchers[:swim] = :can_swim?
+ #
+ # This makes it possible to say:
+ #
+ # person.should swim # passes if person.should_swim? returns true
+ #
+ def predicate_matchers
+ @predicate_matchers ||= {}
+ end
+
+ # Prepends a global <tt>before</tt> block to all behaviours.
+ # See #append_before for filtering semantics.
+ def prepend_before(*args, &proc)
+ Behaviour.prepend_before(*args, &proc)
+ end
+ # Appends a global <tt>before</tt> block to all behaviours.
+ #
+ # If you want to restrict the block to a subset of all the behaviours then
+ # specify this in a Hash as the last argument:
+ #
+ # config.prepend_before(:all, :behaviour_type => :farm)
+ #
+ # or
+ #
+ # config.prepend_before(:behaviour_type => :farm)
+ #
+ def append_before(*args, &proc)
+ Behaviour.append_before(*args, &proc)
+ end
+ alias_method :before, :append_before
+
+ # Prepends a global <tt>after</tt> block to all behaviours.
+ # See #append_before for filtering semantics.
+ def prepend_after(*args, &proc)
+ Behaviour.prepend_after(*args, &proc)
+ end
+ alias_method :after, :prepend_after
+ # Appends a global <tt>after</tt> block to all behaviours.
+ # See #append_before for filtering semantics.
+ def append_after(*args, &proc)
+ Behaviour.append_after(*args, &proc)
+ end
+
+ private
+
+ def mock_framework_path(framework_name)
+ File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "..", "plugins", "mock_frameworks", framework_name))
+ end
+
+ end
+ end
+end
diff --git a/spec/lib/spec/dsl/description.rb b/spec/lib/spec/dsl/description.rb
new file mode 100755
index 000000000..fe8c9b0c9
--- /dev/null
+++ b/spec/lib/spec/dsl/description.rb
@@ -0,0 +1,76 @@
+module Spec
+ module DSL
+ class Description
+ module ClassMethods
+ def generate_description(*args)
+ description = args.shift.to_s
+ unless args.empty?
+ suffix = args.shift.to_s
+ description << " " unless suffix =~ /^\s|\.|#/
+ description << suffix
+ end
+ description
+ end
+ end
+ extend ClassMethods
+
+ attr_reader :description, :described_type
+
+ def initialize(*args)
+ args, @options = args_and_options(*args)
+ init_behaviour_type(@options)
+ init_spec_path(@options)
+ init_described_type(args)
+ init_description(*args)
+ end
+
+ def [](key)
+ @options[key]
+ end
+
+ def []=(key, value)
+ @options[key] = value
+ end
+
+ def to_s; @description; end
+
+ def ==(value)
+ case value
+ when Description
+ @description == value.description
+ else
+ @description == value
+ end
+ end
+
+ private
+ def init_behaviour_type(options)
+ # NOTE - BE CAREFUL IF CHANGING THIS NEXT LINE:
+ # this line is as it is to satisfy JRuby - the original version
+ # read, simply: "if options[:behaviour_class]", which passed against ruby, but failed against jruby
+ if options[:behaviour_class] && options[:behaviour_class].ancestors.include?(Behaviour)
+ options[:behaviour_type] = parse_behaviour_type(@options[:behaviour_class])
+ end
+ end
+
+ def init_spec_path(options)
+ if options.has_key?(:spec_path)
+ options[:spec_path] = File.expand_path(@options[:spec_path])
+ end
+ end
+
+ def init_description(*args)
+ @description = self.class.generate_description(*args)
+ end
+
+ def init_described_type(args)
+ @described_type = args.first unless args.first.is_a?(String)
+ end
+
+ def parse_behaviour_type(behaviour_class)
+ behaviour_class.to_s.split("::").reverse[0].gsub!('Behaviour', '').downcase.to_sym
+ end
+
+ end
+ end
+end
diff --git a/spec/lib/spec/dsl/errors.rb b/spec/lib/spec/dsl/errors.rb
new file mode 100644
index 000000000..ba7046a89
--- /dev/null
+++ b/spec/lib/spec/dsl/errors.rb
@@ -0,0 +1,9 @@
+module Spec
+ module DSL
+ class ExamplePendingError < StandardError
+ end
+
+ class PendingFixedError < StandardError
+ end
+ end
+end
diff --git a/spec/lib/spec/dsl/example.rb b/spec/lib/spec/dsl/example.rb
new file mode 100644
index 000000000..d04073f7e
--- /dev/null
+++ b/spec/lib/spec/dsl/example.rb
@@ -0,0 +1,135 @@
+require 'timeout'
+
+module Spec
+ module DSL
+ class Example
+ # The global sequence number of this example
+ attr_accessor :number
+
+ def initialize(description, options={}, &example_block)
+ @from = caller(0)[3]
+ @options = options
+ @example_block = example_block
+ @description = description
+ @description_generated_proc = lambda { |desc| @generated_description = desc }
+ end
+
+ def run(reporter, before_each_block, after_each_block, dry_run, execution_context, timeout=nil)
+ @dry_run = dry_run
+ reporter.example_started(self)
+ return reporter.example_finished(self) if dry_run
+
+ errors = []
+ location = nil
+ Timeout.timeout(timeout) do
+ before_each_ok = before_example(execution_context, errors, &before_each_block)
+ example_ok = run_example(execution_context, errors) if before_each_ok
+ after_each_ok = after_example(execution_context, errors, &after_each_block)
+ location = failure_location(before_each_ok, example_ok, after_each_ok)
+ end
+
+ ExampleShouldRaiseHandler.new(@from, @options).handle(errors)
+ reporter.example_finished(self, errors.first, location, @example_block.nil?) if reporter
+ end
+
+ def matches?(matcher, specified_examples)
+ matcher.example_desc = description
+ matcher.matches?(specified_examples)
+ end
+
+ def description
+ @description == :__generate_description ? generated_description : @description
+ end
+
+ def to_s
+ description
+ end
+
+ private
+
+ def generated_description
+ return @generated_description if @generated_description
+ if @dry_run
+ "NO NAME (Because of --dry-run)"
+ else
+ if @failed
+ "NO NAME (Because of Error raised in matcher)"
+ else
+ "NO NAME (Because there were no expectations)"
+ end
+ end
+ end
+
+ def before_example(execution_context, errors, &behaviour_before_block)
+ setup_mocks(execution_context)
+ Spec::Matchers.description_generated(@description_generated_proc)
+
+ builder = CompositeProcBuilder.new
+ before_proc = builder.proc(&append_errors(errors))
+ execution_context.instance_eval(&before_proc)
+
+ execution_context.instance_eval(&behaviour_before_block) if behaviour_before_block
+ return errors.empty?
+ rescue Exception => e
+ @failed = true
+ errors << e
+ return false
+ end
+
+ def run_example(execution_context, errors)
+ begin
+ execution_context.instance_eval(&@example_block) if @example_block
+ return true
+ rescue Exception => e
+ @failed = true
+ errors << e
+ return false
+ end
+ end
+
+ def after_example(execution_context, errors, &behaviour_after_each)
+ execution_context.instance_eval(&behaviour_after_each) if behaviour_after_each
+
+ begin
+ verify_mocks(execution_context)
+ ensure
+ teardown_mocks(execution_context)
+ end
+
+ Spec::Matchers.unregister_description_generated(@description_generated_proc)
+
+ builder = CompositeProcBuilder.new
+ after_proc = builder.proc(&append_errors(errors))
+ execution_context.instance_eval(&after_proc)
+
+ return errors.empty?
+ rescue Exception => e
+ @failed = true
+ errors << e
+ return false
+ end
+
+ def setup_mocks(execution_context)
+ execution_context.setup_mocks_for_rspec if execution_context.respond_to?(:setup_mocks_for_rspec)
+ end
+
+ def verify_mocks(execution_context)
+ execution_context.verify_mocks_for_rspec if execution_context.respond_to?(:verify_mocks_for_rspec)
+ end
+
+ def teardown_mocks(execution_context)
+ execution_context.teardown_mocks_for_rspec if execution_context.respond_to?(:teardown_mocks_for_rspec)
+ end
+
+ def append_errors(errors)
+ proc {|error| errors << error}
+ end
+
+ def failure_location(before_each_ok, example_ok, after_each_ok)
+ return 'before(:each)' unless before_each_ok
+ return description unless example_ok
+ return 'after(:each)' unless after_each_ok
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/dsl/example_matcher.rb b/spec/lib/spec/dsl/example_matcher.rb
new file mode 100755
index 000000000..18cc47409
--- /dev/null
+++ b/spec/lib/spec/dsl/example_matcher.rb
@@ -0,0 +1,40 @@
+module Spec
+ module DSL
+ class ExampleMatcher
+
+ attr_writer :example_desc
+ def initialize(behaviour_desc, example_desc=nil)
+ @behaviour_desc = behaviour_desc
+ @example_desc = example_desc
+ end
+
+ def matches?(specified_examples)
+ specified_examples.each do |specified_example|
+ return true if matches_literal_example?(specified_example) || matches_example_not_considering_modules?(specified_example)
+ end
+ false
+ end
+
+ private
+ def matches_literal_example?(specified_example)
+ specified_example =~ /(^#{context_regexp} #{example_regexp}$|^#{context_regexp}$|^#{example_regexp}$)/
+ end
+
+ def matches_example_not_considering_modules?(specified_example)
+ specified_example =~ /(^#{context_regexp_not_considering_modules} #{example_regexp}$|^#{context_regexp_not_considering_modules}$|^#{example_regexp}$)/
+ end
+
+ def context_regexp
+ Regexp.escape(@behaviour_desc)
+ end
+
+ def context_regexp_not_considering_modules
+ Regexp.escape(@behaviour_desc.split('::').last)
+ end
+
+ def example_regexp
+ Regexp.escape(@example_desc)
+ end
+ end
+ end
+end
diff --git a/test/lib/spec/runner/spec_should_raise_handler.rb b/spec/lib/spec/dsl/example_should_raise_handler.rb
index c7fa41c4e..942327317 100644
--- a/test/lib/spec/runner/spec_should_raise_handler.rb
+++ b/spec/lib/spec/dsl/example_should_raise_handler.rb
@@ -1,6 +1,6 @@
module Spec
- module Runner
- class SpecShouldRaiseHandler
+ module DSL
+ class ExampleShouldRaiseHandler
def initialize(file_and_line_number, opts)
@file_and_line_number = file_and_line_number
@options = opts
@@ -31,9 +31,9 @@ module Spec
def build_message(exception=nil)
if @expected_error_message.nil?
- message = "specify block expected #{@expected_error_class.to_s}"
+ message = "example block expected #{@expected_error_class.to_s}"
else
- message = "specify block expected #{@expected_error_class.new(@expected_error_message.to_s).inspect}"
+ message = "example block expected #{@expected_error_class.new(@expected_error_message.to_s).inspect}"
end
message << " but raised #{exception.inspect}" if exception
message << " but nothing was raised" unless exception
diff --git a/test/lib/spec/expectations.rb b/spec/lib/spec/expectations.rb
index cc58bba15..65ea47425 100644
--- a/test/lib/spec/expectations.rb
+++ b/spec/lib/spec/expectations.rb
@@ -1,9 +1,6 @@
-require 'spec/deprecated'
require 'spec/matchers'
-require 'spec/expectations/sugar'
require 'spec/expectations/errors'
require 'spec/expectations/extensions'
-require 'spec/expectations/should'
require 'spec/expectations/handler'
module Spec
@@ -56,4 +53,4 @@ module Spec
end
end
end
-end \ No newline at end of file
+end
diff --git a/test/lib/spec/expectations/differs/default.rb b/spec/lib/spec/expectations/differs/default.rb
index e08325728..87e59b3a6 100644
--- a/test/lib/spec/expectations/differs/default.rb
+++ b/spec/lib/spec/expectations/differs/default.rb
@@ -10,7 +10,6 @@ module Spec
module Expectations
module Differs
- # TODO add colour support
# TODO add some rdoc
class Default
def initialize(format=:unified,context_lines=nil,colour=nil)
diff --git a/test/lib/spec/expectations/errors.rb b/spec/lib/spec/expectations/errors.rb
index 03e81a064..03e81a064 100644
--- a/test/lib/spec/expectations/errors.rb
+++ b/spec/lib/spec/expectations/errors.rb
diff --git a/test/lib/spec/expectations/extensions.rb b/spec/lib/spec/expectations/extensions.rb
index 0381dc7f3..60c9b9e7d 100644
--- a/test/lib/spec/expectations/extensions.rb
+++ b/spec/lib/spec/expectations/extensions.rb
@@ -1,3 +1,2 @@
require 'spec/expectations/extensions/object'
-require 'spec/expectations/extensions/proc'
require 'spec/expectations/extensions/string_and_symbol'
diff --git a/spec/lib/spec/expectations/extensions/object.rb b/spec/lib/spec/expectations/extensions/object.rb
new file mode 100644
index 000000000..f59af722e
--- /dev/null
+++ b/spec/lib/spec/expectations/extensions/object.rb
@@ -0,0 +1,66 @@
+module Spec
+ module Expectations
+ # rspec adds #should and #should_not to every Object (and,
+ # implicitly, every Class).
+ module ObjectExpectations
+
+ # :call-seq:
+ # should(matcher)
+ # should == expected
+ # should === expected
+ # should =~ expected
+ #
+ # receiver.should(matcher)
+ # => Passes if matcher.matches?(receiver)
+ #
+ # receiver.should == expected #any value
+ # => Passes if (receiver == expected)
+ #
+ # receiver.should === expected #any value
+ # => Passes if (receiver === expected)
+ #
+ # receiver.should =~ regexp
+ # => Passes if (receiver =~ regexp)
+ #
+ # See Spec::Matchers for more information about matchers
+ #
+ # == Warning
+ #
+ # NOTE that this does NOT support receiver.should != expected.
+ # Instead, use receiver.should_not == expected
+ def should(matcher=nil, &block)
+ return ExpectationMatcherHandler.handle_matcher(self, matcher, &block) if matcher
+ Spec::Matchers::PositiveOperatorMatcher.new(self)
+ end
+
+ # :call-seq:
+ # should_not(matcher)
+ # should_not == expected
+ # should_not === expected
+ # should_not =~ expected
+ #
+ # receiver.should_not(matcher)
+ # => Passes unless matcher.matches?(receiver)
+ #
+ # receiver.should_not == expected
+ # => Passes unless (receiver == expected)
+ #
+ # receiver.should_not === expected
+ # => Passes unless (receiver === expected)
+ #
+ # receiver.should_not =~ regexp
+ # => Passes unless (receiver =~ regexp)
+ #
+ # See Spec::Matchers for more information about matchers
+ def should_not(matcher=nil, &block)
+ return NegativeExpectationMatcherHandler.handle_matcher(self, matcher, &block) if matcher
+ Spec::Matchers::NegativeOperatorMatcher.new(self)
+ end
+
+ end
+ end
+end
+
+class Object
+ include Spec::Expectations::ObjectExpectations
+end
diff --git a/test/lib/spec/expectations/extensions/string_and_symbol.rb b/spec/lib/spec/expectations/extensions/string_and_symbol.rb
index 30f60d4d0..29cfbddfa 100644
--- a/test/lib/spec/expectations/extensions/string_and_symbol.rb
+++ b/spec/lib/spec/expectations/extensions/string_and_symbol.rb
@@ -2,7 +2,7 @@ module Spec
module Expectations
module StringHelpers
def starts_with?(prefix)
- to_s[0..(prefix.length - 1)] == prefix
+ to_s[0..(prefix.to_s.length - 1)] == prefix.to_s
end
end
end
@@ -14,4 +14,4 @@ end
class Symbol
include Spec::Expectations::StringHelpers
-end \ No newline at end of file
+end
diff --git a/spec/lib/spec/expectations/handler.rb b/spec/lib/spec/expectations/handler.rb
new file mode 100644
index 000000000..4caa321e4
--- /dev/null
+++ b/spec/lib/spec/expectations/handler.rb
@@ -0,0 +1,43 @@
+module Spec
+ module Expectations
+
+ module MatcherHandlerHelper
+ def describe(matcher)
+ matcher.respond_to?(:description) ? matcher.description : "[#{matcher.class.name} does not provide a description]"
+ end
+ end
+
+ class ExpectationMatcherHandler
+ class << self
+ include MatcherHandlerHelper
+ def handle_matcher(actual, matcher, &block)
+ match = matcher.matches?(actual, &block)
+ ::Spec::Matchers.generated_description = "should #{describe(matcher)}"
+ Spec::Expectations.fail_with(matcher.failure_message) unless match
+ end
+ end
+ end
+
+ class NegativeExpectationMatcherHandler
+ class << self
+ include MatcherHandlerHelper
+ def handle_matcher(actual, matcher, &block)
+ unless matcher.respond_to?(:negative_failure_message)
+ Spec::Expectations.fail_with(
+<<-EOF
+Matcher does not support should_not.
+See Spec::Matchers for more information
+about matchers.
+EOF
+)
+ end
+ match = matcher.matches?(actual, &block)
+ ::Spec::Matchers.generated_description = "should not #{describe(matcher)}"
+ Spec::Expectations.fail_with(matcher.negative_failure_message) if match
+ end
+ end
+ end
+
+ end
+end
+
diff --git a/spec/lib/spec/extensions.rb b/spec/lib/spec/extensions.rb
new file mode 100755
index 000000000..824f03bfb
--- /dev/null
+++ b/spec/lib/spec/extensions.rb
@@ -0,0 +1 @@
+require 'spec/extensions/object'
diff --git a/spec/lib/spec/extensions/object.rb b/spec/lib/spec/extensions/object.rb
new file mode 100755
index 000000000..6218aa770
--- /dev/null
+++ b/spec/lib/spec/extensions/object.rb
@@ -0,0 +1,6 @@
+class Object
+ def args_and_options(*args)
+ options = Hash === args.last ? args.pop : {}
+ return args, options
+ end
+end
diff --git a/test/lib/spec/matchers.rb b/spec/lib/spec/matchers.rb
index 9db24d486..fd208d628 100644
--- a/test/lib/spec/matchers.rb
+++ b/spec/lib/spec/matchers.rb
@@ -1,5 +1,3 @@
-require 'spec/deprecated'
-require 'spec/callback'
require 'spec/matchers/be'
require 'spec/matchers/be_close'
require 'spec/matchers/change'
@@ -13,6 +11,7 @@ require 'spec/matchers/raise_error'
require 'spec/matchers/respond_to'
require 'spec/matchers/satisfy'
require 'spec/matchers/throw_symbol'
+require 'spec/matchers/operator_matcher'
module Spec
@@ -62,9 +61,9 @@ module Spec
# You can use this feature to invoke any predicate that begins with "has_", whether it is
# part of the Ruby libraries (like +Hash#has_key?+) or a method you wrote on your own class.
#
- # == Custom Expression Matchers
+ # == Custom Expectation Matchers
#
- # When you find that none of the stock Expression Matchers provide a natural
+ # When you find that none of the stock Expectation Matchers provide a natural
# feeling expectation, you can very easily write your own.
#
# For example, imagine that you are writing a game in which players can
@@ -87,15 +86,15 @@ module Spec
# def initialize(expected)
# @expected = expected
# end
- # def matches?(actual)
- # @actual = actual
- # bob.current_zone.eql?(Zone.new(@expected))
+ # def matches?(target)
+ # @target = target
+ # @target.current_zone.eql?(Zone.new(@expected))
# end
# def failure_message
- # "expected #{@actual.inspect} to be in Zone #{@expected}"
+ # "expected #{@target.inspect} to be in Zone #{@expected}"
# end
# def negative_failure_message
- # "expected #{@actual.inspect} not to be in Zone #{@expected}"
+ # "expected #{@target.inspect} not to be in Zone #{@expected}"
# end
# end
#
@@ -119,18 +118,40 @@ module Spec
# end
# end
#
- # context "Player behaviour" do
+ # describe "Player behaviour" do
# include CustomGameMatchers
# ...
# end
+ #
+ # or you can include in globally in a spec_helper.rb file <tt>require</tt>d
+ # from your spec file(s):
+ #
+ # Spec::Runner.configure do |config|
+ # config.include(CustomGameMatchers)
+ # end
+ #
module Matchers
-
- class << self
- callback_events :description_generated
+ module ModuleMethods
+ def description_generated(callback)
+ description_generated_callbacks << callback
+ end
+
+ def unregister_description_generated(callback)
+ description_generated_callbacks.delete(callback)
+ end
+
def generated_description=(name)
- notify_callbacks(:description_generated, name)
+ description_generated_callbacks.each do |callback|
+ callback.call(name)
+ end
+ end
+
+ private
+ def description_generated_callbacks
+ @description_generated_callbacks ||= []
end
end
+ extend ModuleMethods
def method_missing(sym, *args, &block) # :nodoc:
return Matchers::Be.new(sym, *args) if sym.starts_with?("be_")
@@ -138,23 +159,8 @@ module Spec
super
end
- deprecated do
- # This supports sugar delegating to Matchers
- class Matcher #:nodoc:
- include Matchers
-
- def respond_to?(sym)
- if sym.to_s[0..2] == "be_"
- return true
- else
- super
- end
- end
- end
- end
-
class MatcherError < StandardError
end
end
-end \ No newline at end of file
+end
diff --git a/test/lib/spec/matchers/be.rb b/spec/lib/spec/matchers/be.rb
index 957f23de8..0eb1629a6 100644
--- a/test/lib/spec/matchers/be.rb
+++ b/spec/lib/spec/matchers/be.rb
@@ -2,8 +2,8 @@ module Spec
module Matchers
class Be #:nodoc:
- def initialize(expected=nil, *args)
- @expected = parse_expected(expected)
+ def initialize(*args)
+ @expected = parse_expected(args.shift)
@args = args
@comparison = ""
end
@@ -57,8 +57,24 @@ module Spec
return @actual <= @expected if @less_than_or_equal
return @actual >= @expected if @greater_than_or_equal
return @actual > @expected if @greater_than
+ return @actual == @expected if @double_equal
+ return @actual === @expected if @triple_equal
return @actual.equal?(@expected)
end
+
+ def ==(expected)
+ @double_equal = true
+ @comparison = "== "
+ @expected = expected
+ self
+ end
+
+ def ===(expected)
+ @triple_equal = true
+ @comparison = "=== "
+ @expected = expected
+ self
+ end
def <(expected)
@less_than = true
@@ -89,19 +105,25 @@ module Spec
end
def description
- "be #{@comparison}#{@expected}"
+ "#{prefix_to_sentence}#{comparison}#{expected_to_sentence}#{args_to_sentence}"
end
private
def parse_expected(expected)
if Symbol === expected
- ["be_an_","be_a_","be_"].each do |prefix|
- @handling_predicate = true
- return "#{expected.to_s.sub(prefix,"")}".to_sym if expected.starts_with?(prefix)
+ @handling_predicate = true
+ ["be_an_","be_a_","be_"].each do |@prefix|
+ return "#{expected.to_s.sub(@prefix,"")}".to_sym if expected.starts_with?(@prefix)
end
end
+ @prefix = "be "
return expected
end
+
+ def handling_predicate?
+ return false if [:true, :false, :nil].include?(@expected)
+ return @handling_predicate
+ end
def predicate
"#{@expected.to_s}?".to_sym
@@ -113,14 +135,37 @@ module Spec
def args_to_s
return "" if @args.empty?
- transformed_args = @args.collect{|a| a.inspect}
- return "(#{transformed_args.join(', ')})"
+ inspected_args = @args.collect{|a| a.inspect}
+ return "(#{inspected_args.join(', ')})"
end
- def handling_predicate?
- return false if [:true, :false, :nil].include?(@expected)
- return @handling_predicate
+ def comparison
+ @comparison
+ end
+
+ def expected_to_sentence
+ split_words(@expected)
+ end
+
+ def prefix_to_sentence
+ split_words(@prefix)
end
+
+ def split_words(sym)
+ sym.to_s.gsub(/_/,' ')
+ end
+
+ def args_to_sentence
+ case @args.length
+ when 0
+ ""
+ when 1
+ " #{@args[0]}"
+ else
+ " #{@args[0...-1].join(', ')} and #{@args[-1]}"
+ end
+ end
+
end
# :call-seq:
diff --git a/test/lib/spec/matchers/be_close.rb b/spec/lib/spec/matchers/be_close.rb
index b09e3fd2f..7763eb97e 100644
--- a/test/lib/spec/matchers/be_close.rb
+++ b/spec/lib/spec/matchers/be_close.rb
@@ -13,11 +13,11 @@ module Spec
end
def failure_message
- "expected #{@expected} +/- (<#{@delta}), got #{@actual}"
+ "expected #{@expected} +/- (< #{@delta}), got #{@actual}"
end
def description
- "be close to #{@expected} (+- #{@delta})"
+ "be close to #{@expected} (within +- #{@delta})"
end
end
@@ -34,4 +34,4 @@ module Spec
Matchers::BeClose.new(expected, delta)
end
end
-end \ No newline at end of file
+end
diff --git a/test/lib/spec/matchers/change.rb b/spec/lib/spec/matchers/change.rb
index 41a718aca..41a718aca 100644
--- a/test/lib/spec/matchers/change.rb
+++ b/spec/lib/spec/matchers/change.rb
diff --git a/test/lib/spec/matchers/eql.rb b/spec/lib/spec/matchers/eql.rb
index caca1f7c6..280ca5454 100644
--- a/test/lib/spec/matchers/eql.rb
+++ b/spec/lib/spec/matchers/eql.rb
@@ -40,4 +40,4 @@ module Spec
Matchers::Eql.new(expected)
end
end
-end \ No newline at end of file
+end
diff --git a/test/lib/spec/matchers/equal.rb b/spec/lib/spec/matchers/equal.rb
index e987e73cb..4bfc74951 100644
--- a/test/lib/spec/matchers/equal.rb
+++ b/spec/lib/spec/matchers/equal.rb
@@ -40,4 +40,4 @@ module Spec
Matchers::Equal.new(expected)
end
end
-end \ No newline at end of file
+end
diff --git a/test/lib/spec/matchers/has.rb b/spec/lib/spec/matchers/has.rb
index cc5a250b8..cc5a250b8 100644
--- a/test/lib/spec/matchers/has.rb
+++ b/spec/lib/spec/matchers/has.rb
diff --git a/test/lib/spec/matchers/have.rb b/spec/lib/spec/matchers/have.rb
index 81f9af3e3..f28b86ad3 100644
--- a/test/lib/spec/matchers/have.rb
+++ b/spec/lib/spec/matchers/have.rb
@@ -23,54 +23,56 @@ module Spec
end
def matches?(collection_owner)
- if collection_owner.respond_to?(collection_name)
- collection = collection_owner.send(collection_name, *@args, &@block)
+ if collection_owner.respond_to?(@collection_name)
+ collection = collection_owner.send(@collection_name, *@args, &@block)
elsif (collection_owner.respond_to?(:length) || collection_owner.respond_to?(:size))
collection = collection_owner
else
- collection_owner.send(collection_name, *@args, &@block)
+ collection_owner.send(@collection_name, *@args, &@block)
end
- @actual = collection.length if collection.respond_to?(:length)
@actual = collection.size if collection.respond_to?(:size)
+ @actual = collection.length if collection.respond_to?(:length)
+ raise not_a_collection if @actual.nil?
return @actual >= @expected if @relativity == :at_least
return @actual <= @expected if @relativity == :at_most
return @actual == @expected
end
+
+ def not_a_collection
+ "expected #{@collection_name} to be a collection but it does not respond to #length or #size"
+ end
def failure_message
- "expected #{relative_expectation} #{collection_name}, got #{@actual}"
+ "expected #{relative_expectation} #{@collection_name}, got #{@actual}"
end
def negative_failure_message
if @relativity == :exactly
- return "expected target not to have #{@expected} #{collection_name}, got #{@actual}"
+ return "expected target not to have #{@expected} #{@collection_name}, got #{@actual}"
elsif @relativity == :at_most
return <<-EOF
Isn't life confusing enough?
Instead of having to figure out the meaning of this:
- should_not have_at_most(#{@expected}).#{collection_name}
+ should_not have_at_most(#{@expected}).#{@collection_name}
We recommend that you use this instead:
- should have_at_least(#{@expected + 1}).#{collection_name}
+ should have_at_least(#{@expected + 1}).#{@collection_name}
EOF
elsif @relativity == :at_least
return <<-EOF
Isn't life confusing enough?
Instead of having to figure out the meaning of this:
- should_not have_at_least(#{@expected}).#{collection_name}
+ should_not have_at_least(#{@expected}).#{@collection_name}
We recommend that you use this instead:
- should have_at_most(#{@expected - 1}).#{collection_name}
+ should have_at_most(#{@expected - 1}).#{@collection_name}
EOF
end
end
def description
- "have #{relative_expectation} #{collection_name}"
+ "have #{relative_expectation} #{@collection_name}"
end
private
- def collection_name
- @collection_name
- end
def relative_expectation
"#{relativities[@relativity]}#{@expected}"
@@ -137,4 +139,4 @@ EOF
Matchers::Have.new(n, :at_most)
end
end
-end \ No newline at end of file
+end
diff --git a/spec/lib/spec/matchers/include.rb b/spec/lib/spec/matchers/include.rb
new file mode 100644
index 000000000..5476f97d8
--- /dev/null
+++ b/spec/lib/spec/matchers/include.rb
@@ -0,0 +1,70 @@
+module Spec
+ module Matchers
+
+ class Include #:nodoc:
+
+ def initialize(*expecteds)
+ @expecteds = expecteds
+ end
+
+ def matches?(actual)
+ @actual = actual
+ @expecteds.each do |expected|
+ return false unless actual.include?(expected)
+ end
+ true
+ end
+
+ def failure_message
+ _message
+ end
+
+ def negative_failure_message
+ _message("not ")
+ end
+
+ def description
+ "include #{_pretty_print(@expecteds)}"
+ end
+
+ private
+ def _message(maybe_not="")
+ "expected #{@actual.inspect} #{maybe_not}to include #{_pretty_print(@expecteds)}"
+ end
+
+ def _pretty_print(array)
+ result = ""
+ array.each_with_index do |item, index|
+ if index < (array.length - 2)
+ result << "#{item.inspect}, "
+ elsif index < (array.length - 1)
+ result << "#{item.inspect} and "
+ else
+ result << "#{item.inspect}"
+ end
+ end
+ result
+ end
+ end
+
+ # :call-seq:
+ # should include(expected)
+ # should_not include(expected)
+ #
+ # Passes if actual includes expected. This works for
+ # collections and Strings. You can also pass in multiple args
+ # and it will only pass if all args are found in collection.
+ #
+ # == Examples
+ #
+ # [1,2,3].should include(3)
+ # [1,2,3].should include(2,3) #would pass
+ # [1,2,3].should include(2,3,4) #would fail
+ # [1,2,3].should_not include(4)
+ # "spread".should include("read")
+ # "spread".should_not include("red")
+ def include(*expected)
+ Matchers::Include.new(*expected)
+ end
+ end
+end
diff --git a/test/lib/spec/matchers/match.rb b/spec/lib/spec/matchers/match.rb
index 61ab52429..61ab52429 100644
--- a/test/lib/spec/matchers/match.rb
+++ b/spec/lib/spec/matchers/match.rb
diff --git a/spec/lib/spec/matchers/operator_matcher.rb b/spec/lib/spec/matchers/operator_matcher.rb
new file mode 100755
index 000000000..2d47ea85a
--- /dev/null
+++ b/spec/lib/spec/matchers/operator_matcher.rb
@@ -0,0 +1,72 @@
+module Spec
+ module Matchers
+ class BaseOperatorMatcher
+
+ def initialize(target)
+ @target = target
+ end
+
+ def ==(expected)
+ @expected = expected
+ __delegate_method_missing_to_target("==", expected)
+ end
+
+ def ===(expected)
+ @expected = expected
+ __delegate_method_missing_to_target("===", expected)
+ end
+
+ def =~(expected)
+ @expected = expected
+ __delegate_method_missing_to_target("=~", expected)
+ end
+
+ def >(expected)
+ @expected = expected
+ __delegate_method_missing_to_target(">", expected)
+ end
+
+ def >=(expected)
+ @expected = expected
+ __delegate_method_missing_to_target(">=", expected)
+ end
+
+ def <(expected)
+ @expected = expected
+ __delegate_method_missing_to_target("<", expected)
+ end
+
+ def <=(expected)
+ @expected = expected
+ __delegate_method_missing_to_target("<=", expected)
+ end
+
+ def fail_with_message(message)
+ Spec::Expectations.fail_with(message, @expected, @target)
+ end
+
+ end
+
+ class PositiveOperatorMatcher < BaseOperatorMatcher #:nodoc:
+
+ def __delegate_method_missing_to_target(operator, expected)
+ ::Spec::Matchers.generated_description = "should #{operator} #{expected.inspect}"
+ return if @target.send(operator, expected)
+ return fail_with_message("expected: #{expected.inspect},\n got: #{@target.inspect} (using #{operator})") if ['==','===', '=~'].include?(operator)
+ return fail_with_message("expected: #{operator} #{expected.inspect},\n got: #{operator.gsub(/./, ' ')} #{@target.inspect}")
+ end
+
+ end
+
+ class NegativeOperatorMatcher < BaseOperatorMatcher #:nodoc:
+
+ def __delegate_method_missing_to_target(operator, expected)
+ ::Spec::Matchers.generated_description = "should not #{operator} #{expected.inspect}"
+ return unless @target.send(operator, expected)
+ return fail_with_message("expected not: #{operator} #{expected.inspect},\n got: #{operator.gsub(/./, ' ')} #{@target.inspect}")
+ end
+
+ end
+
+ end
+end
diff --git a/test/lib/spec/matchers/raise_error.rb b/spec/lib/spec/matchers/raise_error.rb
index 95e82ad5e..b45dcf65c 100644
--- a/test/lib/spec/matchers/raise_error.rb
+++ b/spec/lib/spec/matchers/raise_error.rb
@@ -2,9 +2,14 @@ module Spec
module Matchers
class RaiseError #:nodoc:
- def initialize(exception=Exception, message=nil)
- @expected_error = exception
- @expected_message = message
+ def initialize(error_or_message=Exception, message=nil)
+ if String === error_or_message
+ @expected_error = Exception
+ @expected_message = error_or_message
+ else
+ @expected_error = error_or_message
+ @expected_message = message
+ end
end
def matches?(proc)
@@ -24,7 +29,7 @@ module Spec
@raised_other = true
end
else
- if @actual_error.message == @expected_message
+ if @expected_message == @actual_error.message
@raised_expected_error = true
else
@raised_other = true
diff --git a/spec/lib/spec/matchers/respond_to.rb b/spec/lib/spec/matchers/respond_to.rb
new file mode 100644
index 000000000..3d23422aa
--- /dev/null
+++ b/spec/lib/spec/matchers/respond_to.rb
@@ -0,0 +1,45 @@
+module Spec
+ module Matchers
+
+ class RespondTo #:nodoc:
+ def initialize(*names)
+ @names = names
+ @names_not_responded_to = []
+ end
+
+ def matches?(target)
+ @names.each do |name|
+ unless target.respond_to?(name)
+ @names_not_responded_to << name
+ end
+ end
+ return @names_not_responded_to.empty?
+ end
+
+ def failure_message
+ "expected target to respond to #{@names_not_responded_to.collect {|name| name.inspect }.join(', ')}"
+ end
+
+ def negative_failure_message
+ "expected target not to respond to #{@names.collect {|name| name.inspect }.join(', ')}"
+ end
+
+ def description
+ "respond to ##{@names.to_s}"
+ end
+ end
+
+ # :call-seq:
+ # should respond_to(*names)
+ # should_not respond_to(*names)
+ #
+ # Matches if the target object responds to all of the names
+ # provided. Names can be Strings or Symbols.
+ #
+ # == Examples
+ #
+ def respond_to(*names)
+ Matchers::RespondTo.new(*names)
+ end
+ end
+end
diff --git a/test/lib/spec/matchers/satisfy.rb b/spec/lib/spec/matchers/satisfy.rb
index 6c0ca95bc..6c0ca95bc 100644
--- a/test/lib/spec/matchers/satisfy.rb
+++ b/spec/lib/spec/matchers/satisfy.rb
diff --git a/test/lib/spec/matchers/throw_symbol.rb b/spec/lib/spec/matchers/throw_symbol.rb
index 6732f6fed..6d047bc39 100644
--- a/test/lib/spec/matchers/throw_symbol.rb
+++ b/spec/lib/spec/matchers/throw_symbol.rb
@@ -10,7 +10,7 @@ module Spec
begin
proc.call
rescue NameError => e
- @actual = extract_sym_from_name_error(e)
+ @actual = e.name.to_sym
ensure
if @expected.nil?
return @actual.nil? ? false : true
@@ -46,9 +46,6 @@ module Spec
@expected.nil? ? "a Symbol" : @expected.inspect
end
- def extract_sym_from_name_error(error)
- return "#{error.message.split("`").last.split("'").first}".to_sym
- end
end
# :call-seq:
diff --git a/test/lib/spec/mocks.rb b/spec/lib/spec/mocks.rb
index d0a5d0299..66cbafb3c 100644
--- a/test/lib/spec/mocks.rb
+++ b/spec/lib/spec/mocks.rb
@@ -1,5 +1,7 @@
require 'spec/mocks/methods'
-require 'spec/mocks/mock_handler'
+require 'spec/mocks/argument_constraint_matchers'
+require 'spec/mocks/spec_methods'
+require 'spec/mocks/proxy'
require 'spec/mocks/mock'
require 'spec/mocks/argument_expectation'
require 'spec/mocks/message_expectation'
@@ -7,6 +9,8 @@ require 'spec/mocks/order_group'
require 'spec/mocks/errors'
require 'spec/mocks/error_generator'
require 'spec/mocks/extensions/object'
+require 'spec/mocks/space'
+
module Spec
# == Mocks and Stubs
@@ -125,13 +129,12 @@ module Spec
# In addition, Spec::Mocks adds some keyword Symbols that you can use to
# specify certain kinds of arguments:
#
- # my_mock.should_receive(:sym).with(:no_args)
- # my_mock.should_receive(:sym).with(:any_args)
- # my_mock.should_receive(:sym).with(1, :numeric, "b") #2nd argument can any type of Numeric
- # my_mock.should_receive(:sym).with(1, :boolean, "b") #2nd argument can true or false
- # my_mock.should_receive(:sym).with(1, :string, "b") #2nd argument can be any String
+ # my_mock.should_receive(:sym).with(no_args())
+ # my_mock.should_receive(:sym).with(any_args())
+ # my_mock.should_receive(:sym).with(1, an_instance_of(Numeric), "b") #2nd argument can any type of Numeric
+ # my_mock.should_receive(:sym).with(1, boolean(), "b") #2nd argument can true or false
# my_mock.should_receive(:sym).with(1, /abc/, "b") #2nd argument can be any String matching the submitted Regexp
- # my_mock.should_receive(:sym).with(1, :anything, "b") #2nd argument can be anything at all
+ # my_mock.should_receive(:sym).with(1, anything(), "b") #2nd argument can be anything at all
# my_mock.should_receive(:sym).with(1, ducktype(:abs, :div), "b")
# #2nd argument can be object that responds to #abs and #div
#
@@ -201,32 +204,5 @@ module Spec
#
# my_mock.should_receive(:<<).with("illegal value").once.and_raise(ArgumentError)
module Mocks
- # Shortcut for creating an instance of Spec::Mocks::Mock.
- def mock(name, options={})
- Spec::Mocks::Mock.new(name, options)
- end
-
- # Shortcut for creating an instance of Spec::Mocks::Mock with
- # predefined method stubs.
- #
- # == Examples
- #
- # stub_thing = stub("thing", :a => "A")
- # stub_thing.a == "A" => true
- #
- # stub_person = stub("thing", :name => "Joe", :email => "joe@domain.com")
- # stub_person.name => "Joe"
- # stub_person.email => "joe@domain.com"
- def stub(name, stubs={})
- object_stub = mock(name)
- stubs.each { |key, value| object_stub.stub!(key).and_return(value) }
- object_stub
- end
-
- # Shortcut for creating an instance of Spec::Mocks::DuckTypeArgConstraint
- def duck_type(*args)
- return Spec::Mocks::DuckTypeArgConstraint.new(*args)
- end
-
end
-end \ No newline at end of file
+end
diff --git a/spec/lib/spec/mocks/argument_constraint_matchers.rb b/spec/lib/spec/mocks/argument_constraint_matchers.rb
new file mode 100644
index 000000000..0e4777082
--- /dev/null
+++ b/spec/lib/spec/mocks/argument_constraint_matchers.rb
@@ -0,0 +1,27 @@
+module Spec
+ module Mocks
+ module ArgumentConstraintMatchers
+
+ # Shortcut for creating an instance of Spec::Mocks::DuckTypeArgConstraint
+ def duck_type(*args)
+ DuckTypeArgConstraint.new(*args)
+ end
+
+ def any_args
+ AnyArgsConstraint.new
+ end
+
+ def anything
+ AnyArgConstraint.new(nil)
+ end
+
+ def boolean
+ BooleanArgConstraint.new(nil)
+ end
+
+ def no_args
+ NoArgsConstraint.new
+ end
+ end
+ end
+end
diff --git a/test/lib/spec/mocks/argument_expectation.rb b/spec/lib/spec/mocks/argument_expectation.rb
index a4870e767..5da069b87 100644
--- a/test/lib/spec/mocks/argument_expectation.rb
+++ b/spec/lib/spec/mocks/argument_expectation.rb
@@ -36,11 +36,32 @@ module Spec
def initialize(ignore)
end
+ def ==(other)
+ true
+ end
+
+ # TODO - need this?
def matches?(value)
true
end
end
+ class AnyArgsConstraint
+ def description
+ "any args"
+ end
+ end
+
+ class NoArgsConstraint
+ def description
+ "no args"
+ end
+
+ def ==(args)
+ args == []
+ end
+ end
+
class NumericArgConstraint
def initialize(ignore)
end
@@ -54,6 +75,10 @@ module Spec
def initialize(ignore)
end
+ def ==(value)
+ matches?(value)
+ end
+
def matches?(value)
return true if value.is_a?(TrueClass)
return true if value.is_a?(FalseClass)
@@ -71,12 +96,16 @@ module Spec
end
class DuckTypeArgConstraint
- def initialize(*methods_to_respond_do)
- @methods_to_respond_do = methods_to_respond_do
+ def initialize(*methods_to_respond_to)
+ @methods_to_respond_to = methods_to_respond_to
end
def matches?(value)
- @methods_to_respond_do.all? { |sym| value.respond_to?(sym) }
+ @methods_to_respond_to.all? { |sym| value.respond_to?(sym) }
+ end
+
+ def description
+ "duck_type"
end
end
@@ -90,8 +119,14 @@ module Spec
def initialize(args)
@args = args
- if [:any_args] == args then @expected_params = nil
- elsif [:no_args] == args then @expected_params = []
+ if [:any_args] == args
+ @expected_params = nil
+ warn_deprecated(:any_args.inspect, "any_args()")
+ elsif args.length == 1 && args[0].is_a?(AnyArgsConstraint) then @expected_params = nil
+ elsif [:no_args] == args
+ @expected_params = []
+ warn_deprecated(:no_args.inspect, "no_args()")
+ elsif args.length == 1 && args[0].is_a?(NoArgsConstraint) then @expected_params = []
else @expected_params = process_arg_constraints(args)
end
end
@@ -102,9 +137,25 @@ module Spec
end
end
+ def warn_deprecated(deprecated_method, instead)
+ STDERR.puts "The #{deprecated_method} constraint is deprecated. Use #{instead} instead."
+ end
+
def convert_constraint(constraint)
- return @@constraint_classes[constraint].new(constraint) if constraint.is_a?(Symbol)
- return constraint if constraint.is_a?(DuckTypeArgConstraint)
+ if [:anything, :numeric, :boolean, :string].include?(constraint)
+ case constraint
+ when :anything
+ instead = "anything()"
+ when :boolean
+ instead = "boolean()"
+ when :numeric
+ instead = "an_instance_of(Numeric)"
+ when :string
+ instead = "an_instance_of(String)"
+ end
+ warn_deprecated(constraint.inspect, instead)
+ return @@constraint_classes[constraint].new(constraint)
+ end
return MatcherConstraint.new(constraint) if is_matcher?(constraint)
return RegexpArgConstraint.new(constraint) if constraint.is_a?(Regexp)
return LiteralArgConstraint.new(constraint)
diff --git a/test/lib/spec/mocks/error_generator.rb b/spec/lib/spec/mocks/error_generator.rb
index 950864a7a..01d8f720d 100644
--- a/test/lib/spec/mocks/error_generator.rb
+++ b/spec/lib/spec/mocks/error_generator.rb
@@ -17,8 +17,7 @@ module Spec
end
def raise_unexpected_message_args_error(expectation, *args)
- #this is either :no_args or an Array
- expected_args = (expectation.expected_args == :no_args ? "(no args)" : format_args(*expectation.expected_args))
+ expected_args = format_args(*expectation.expected_args)
actual_args = args.empty? ? "(no args)" : format_args(*args)
__raise "#{intro} expected #{expectation.sym.inspect} with #{expected_args} but received it with #{actual_args}"
end
@@ -45,7 +44,7 @@ module Spec
private
def intro
- @name ? "Mock '#{@name}'" : @target.to_s
+ @name ? "Mock '#{@name}'" : @target.inspect
end
def __raise(message)
@@ -82,4 +81,4 @@ module Spec
end
end
-end \ No newline at end of file
+end
diff --git a/test/lib/spec/mocks/errors.rb b/spec/lib/spec/mocks/errors.rb
index 68fdfe006..68fdfe006 100644
--- a/test/lib/spec/mocks/errors.rb
+++ b/spec/lib/spec/mocks/errors.rb
diff --git a/test/lib/spec/mocks/extensions/object.rb b/spec/lib/spec/mocks/extensions/object.rb
index 4b7531066..4b7531066 100644
--- a/test/lib/spec/mocks/extensions/object.rb
+++ b/spec/lib/spec/mocks/extensions/object.rb
diff --git a/test/lib/spec/mocks/message_expectation.rb b/spec/lib/spec/mocks/message_expectation.rb
index 152e65a47..74ade3c58 100644
--- a/test/lib/spec/mocks/message_expectation.rb
+++ b/spec/lib/spec/mocks/message_expectation.rb
@@ -13,7 +13,7 @@ module Spec
@return_block = lambda {}
@received_count = 0
@expected_received_count = expected_received_count
- @args_expectation = ArgumentExpectation.new([:any_args])
+ @args_expectation = ArgumentExpectation.new([AnyArgsConstraint.new])
@consecutive = false
@exception_to_raise = nil
@symbol_to_throw = nil
@@ -42,6 +42,17 @@ module Spec
@return_block = block_given? ? return_block : lambda { value }
end
+ # :call-seq:
+ # and_raise()
+ # and_raise(Exception) #any exception class
+ # and_raise(exception) #any exception object
+ #
+ # == Warning
+ #
+ # When you pass an exception class, the MessageExpectation will
+ # raise an instance of it, creating it with +new+. If the exception
+ # class initializer requires any parameters, you must pass in an
+ # instance and not the class.
def and_raise(exception=Exception)
@exception_to_raise = exception
end
diff --git a/spec/lib/spec/mocks/methods.rb b/spec/lib/spec/mocks/methods.rb
new file mode 100644
index 000000000..3d898cf31
--- /dev/null
+++ b/spec/lib/spec/mocks/methods.rb
@@ -0,0 +1,39 @@
+module Spec
+ module Mocks
+ module Methods
+ def should_receive(sym, opts={}, &block)
+ __mock_proxy.add_message_expectation(opts[:expected_from] || caller(1)[0], sym.to_sym, opts, &block)
+ end
+
+ def should_not_receive(sym, &block)
+ __mock_proxy.add_negative_message_expectation(caller(1)[0], sym.to_sym, &block)
+ end
+
+ def stub!(sym)
+ __mock_proxy.add_stub(caller(1)[0], sym.to_sym)
+ end
+
+ def received_message?(sym, *args, &block) #:nodoc:
+ __mock_proxy.received_message?(sym.to_sym, *args, &block)
+ end
+
+ def rspec_verify #:nodoc:
+ __mock_proxy.verify
+ end
+
+ def rspec_reset #:nodoc:
+ __mock_proxy.reset
+ end
+
+ private
+
+ def __mock_proxy
+ if Mock === self
+ @mock_proxy ||= Proxy.new(self, @name, @options)
+ else
+ @mock_proxy ||= Proxy.new(self, self.class.name)
+ end
+ end
+ end
+ end
+end
diff --git a/test/lib/spec/mocks/mock.rb b/spec/lib/spec/mocks/mock.rb
index 68de11ff4..aa380e0af 100644
--- a/test/lib/spec/mocks/mock.rb
+++ b/spec/lib/spec/mocks/mock.rb
@@ -12,15 +12,18 @@ module Spec
end
def method_missing(sym, *args, &block)
- __mock_handler.instance_eval {@messages_received << [sym, args, block]}
+ __mock_proxy.instance_eval {@messages_received << [sym, args, block]}
begin
- return self if __mock_handler.null_object?
+ return self if __mock_proxy.null_object?
super(sym, *args, &block)
rescue NoMethodError
- __mock_handler.raise_unexpected_message_error sym, *args
+ __mock_proxy.raise_unexpected_message_error sym, *args
end
end
+ def inspect
+ "#<#{self.class}:#{sprintf '0x%x', self.object_id} @name=#{@name.inspect}>"
+ end
end
end
-end \ No newline at end of file
+end
diff --git a/test/lib/spec/mocks/order_group.rb b/spec/lib/spec/mocks/order_group.rb
index 9983207eb..9983207eb 100644
--- a/test/lib/spec/mocks/order_group.rb
+++ b/spec/lib/spec/mocks/order_group.rb
diff --git a/test/lib/spec/mocks/mock_handler.rb b/spec/lib/spec/mocks/proxy.rb
index ef6f97a1c..6c79d1068 100644
--- a/test/lib/spec/mocks/mock_handler.rb
+++ b/spec/lib/spec/mocks/proxy.rb
@@ -1,9 +1,8 @@
module Spec
module Mocks
- class MockHandler
+ class Proxy
DEFAULT_OPTIONS = {
:null_object => false,
- :auto_verify => true
}
def initialize(target, name, options={})
@@ -23,19 +22,19 @@ module Spec
end
def add_message_expectation(expected_from, sym, opts={}, &block)
- __add expected_from, sym, block
+ __add sym, block
@expectations << MessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, block_given? ? block : nil, 1, opts)
@expectations.last
end
def add_negative_message_expectation(expected_from, sym, &block)
- __add expected_from, sym, block
+ __add sym, block
@expectations << NegativeMessageExpectation.new(@error_generator, @expectation_ordering, expected_from, sym, block_given? ? block : nil)
@expectations.last
end
def add_stub(expected_from, sym)
- __add expected_from, sym, nil
+ __add sym, nil
@stubs.unshift MethodStub.new(@error_generator, @expectation_ordering, expected_from, sym, nil)
@stubs.first
end
@@ -84,24 +83,22 @@ module Spec
@error_generator.raise_unexpected_message_error sym, *args
end
- private
+ private
- def __add(expected_from, sym, block)
- # TODO - this is the only reference in the 'spec/mocks' to the Runner
- current_spec = Runner::Specification.current
- current_spec.after_teardown {verify} if current_spec && @options[:auto_verify]
+ def __add(sym, block)
+ $rspec_mocks.add(@target) unless $rspec_mocks.nil?
define_expected_method(sym)
end
def define_expected_method(sym)
if target_responds_to?(sym) && !@proxied_methods.include?(sym)
+ metaclass.__send__(:alias_method, munge(sym), sym) if metaclass.instance_methods.include?(sym.to_s)
@proxied_methods << sym
- metaclass.__send__(:alias_method, munge(sym), sym)
end
-
+
metaclass_eval(<<-EOF, __FILE__, __LINE__)
def #{sym}(*args, &block)
- __mock_handler.message_received :#{sym}, *args, &block
+ __mock_proxy.message_received :#{sym}, *args, &block
end
EOF
end
@@ -144,8 +141,12 @@ module Spec
def reset_proxied_methods
@proxied_methods.each do |sym|
- metaclass.__send__(:alias_method, sym, munge(sym))
- metaclass.__send__(:undef_method, munge(sym))
+ if metaclass.instance_methods.include?(munge(sym).to_s)
+ metaclass.__send__(:alias_method, sym, munge(sym))
+ metaclass.__send__(:undef_method, munge(sym))
+ else
+ metaclass.__send__(:undef_method, sym)
+ end
end
end
@@ -163,4 +164,4 @@ module Spec
end
end
-end \ No newline at end of file
+end
diff --git a/spec/lib/spec/mocks/space.rb b/spec/lib/spec/mocks/space.rb
new file mode 100644
index 000000000..e04bc5ccb
--- /dev/null
+++ b/spec/lib/spec/mocks/space.rb
@@ -0,0 +1,28 @@
+module Spec
+ module Mocks
+ class Space
+ def add(obj)
+ mocks << obj unless mocks.include?(obj)
+ end
+
+ def verify_all
+ mocks.each do |mock|
+ mock.rspec_verify
+ end
+ end
+
+ def reset_all
+ mocks.each do |mock|
+ mock.rspec_reset
+ end
+ mocks.clear
+ end
+
+ private
+
+ def mocks
+ @mocks ||= []
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/mocks/spec_methods.rb b/spec/lib/spec/mocks/spec_methods.rb
new file mode 100644
index 000000000..fd67fd210
--- /dev/null
+++ b/spec/lib/spec/mocks/spec_methods.rb
@@ -0,0 +1,30 @@
+module Spec
+ module Mocks
+ module SpecMethods
+ include Spec::Mocks::ArgumentConstraintMatchers
+
+ # Shortcut for creating an instance of Spec::Mocks::Mock.
+ def mock(name, options={})
+ Spec::Mocks::Mock.new(name, options)
+ end
+
+ # Shortcut for creating an instance of Spec::Mocks::Mock with
+ # predefined method stubs.
+ #
+ # == Examples
+ #
+ # stub_thing = stub("thing", :a => "A")
+ # stub_thing.a == "A" => true
+ #
+ # stub_person = stub("thing", :name => "Joe", :email => "joe@domain.com")
+ # stub_person.name => "Joe"
+ # stub_person.email => "joe@domain.com"
+ def stub(name, stubs={})
+ object_stub = mock(name)
+ stubs.each { |key, value| object_stub.stub!(key).and_return(value) }
+ object_stub
+ end
+
+ end
+ end
+end
diff --git a/spec/lib/spec/rake/spectask.rb b/spec/lib/spec/rake/spectask.rb
new file mode 100644
index 000000000..f8c6809a9
--- /dev/null
+++ b/spec/lib/spec/rake/spectask.rb
@@ -0,0 +1,217 @@
+#!/usr/bin/env ruby
+
+# Define a task library for running RSpec contexts.
+
+require 'rake'
+require 'rake/tasklib'
+
+module Spec
+ module Rake
+
+ # A Rake task that runs a set of RSpec contexts.
+ #
+ # Example:
+ #
+ # Spec::Rake::SpecTask.new do |t|
+ # t.warning = true
+ # t.rcov = true
+ # end
+ #
+ # This will create a task that can be run with:
+ #
+ # rake spec
+ #
+ # If rake is invoked with a "SPEC=filename" command line option,
+ # then the list of spec files will be overridden to include only the
+ # filename specified on the command line. This provides an easy way
+ # to run just one spec.
+ #
+ # If rake is invoked with a "SPEC_OPTS=options" command line option,
+ # then the given options will override the value of the +spec_opts+
+ # attribute.
+ #
+ # If rake is invoked with a "RCOV_OPTS=options" command line option,
+ # then the given options will override the value of the +rcov_opts+
+ # attribute.
+ #
+ # Examples:
+ #
+ # rake spec # run specs normally
+ # rake spec SPEC=just_one_file.rb # run just one spec file.
+ # rake spec SPEC_OPTS="--diff" # enable diffing
+ # rake spec RCOV_OPTS="--aggregate myfile.txt" # see rcov --help for details
+ #
+ # Each attribute of this task may be a proc. This allows for lazy evaluation,
+ # which is sometimes handy if you want to defer the evaluation of an attribute value
+ # until the task is run (as opposed to when it is defined).
+ class SpecTask < ::Rake::TaskLib
+ class << self
+ def attr_accessor(*names)
+ super(*names)
+ names.each do |name|
+ module_eval "def #{name}() evaluate(@#{name}) end" # Allows use of procs
+ end
+ end
+ end
+
+ # Name of spec task. (default is :spec)
+ attr_accessor :name
+
+ # Array of directories to be added to $LOAD_PATH before running the
+ # specs. Defaults to ['<the absolute path to RSpec's lib directory>']
+ attr_accessor :libs
+
+ # If true, requests that the specs be run with the warning flag set.
+ # E.g. warning=true implies "ruby -w" used to run the specs. Defaults to false.
+ attr_accessor :warning
+
+ # Glob pattern to match spec files. (default is 'spec/**/*_spec.rb')
+ # Setting the SPEC environment variable overrides this.
+ attr_accessor :pattern
+
+ # Array of commandline options to pass to RSpec. Defaults to [].
+ # Setting the SPEC_OPTS environment variable overrides this.
+ attr_accessor :spec_opts
+
+ # Whether or not to use RCov (default is false)
+ # See http://eigenclass.org/hiki.rb?rcov
+ attr_accessor :rcov
+
+ # Array of commandline options to pass to RCov. Defaults to ['--exclude', 'lib\/spec,bin\/spec'].
+ # Ignored if rcov=false
+ # Setting the RCOV_OPTS environment variable overrides this.
+ attr_accessor :rcov_opts
+
+ # Directory where the RCov report is written. Defaults to "coverage"
+ # Ignored if rcov=false
+ attr_accessor :rcov_dir
+
+ # Array of commandline options to pass to ruby. Defaults to [].
+ attr_accessor :ruby_opts
+
+ # Whether or not to fail Rake when an error occurs (typically when specs fail).
+ # Defaults to true.
+ attr_accessor :fail_on_error
+
+ # A message to print to stderr when there are failures.
+ attr_accessor :failure_message
+
+ # Where RSpec's output is written. Defaults to STDOUT.
+ # DEPRECATED. Use --format FORMAT:WHERE in spec_opts.
+ attr_accessor :out
+
+ # Explicitly define the list of spec files to be included in a
+ # spec. +spec_files+ is expected to be an array of file names (a
+ # FileList is acceptable). If both +pattern+ and +spec_files+ are
+ # used, then the list of spec files is the union of the two.
+ # Setting the SPEC environment variable overrides this.
+ attr_accessor :spec_files
+
+ # Defines a new task, using the name +name+.
+ def initialize(name=:spec)
+ @name = name
+ @libs = [File.expand_path(File.dirname(__FILE__) + '/../../../lib')]
+ @pattern = nil
+ @spec_files = nil
+ @spec_opts = []
+ @warning = false
+ @ruby_opts = []
+ @fail_on_error = true
+ @rcov = false
+ @rcov_opts = ['--exclude', 'lib\/spec,bin\/spec,config\/boot.rb']
+ @rcov_dir = "coverage"
+
+ yield self if block_given?
+ @pattern = 'spec/**/*_spec.rb' if pattern.nil? && spec_files.nil?
+ define
+ end
+
+ def define # :nodoc:
+ spec_script = File.expand_path(File.dirname(__FILE__) + '/../../../bin/spec')
+
+ lib_path = libs.join(File::PATH_SEPARATOR)
+ actual_name = Hash === name ? name.keys.first : name
+ unless ::Rake.application.last_comment
+ desc "Run specs" + (rcov ? " using RCov" : "")
+ end
+ task name do
+ RakeFileUtils.verbose(verbose) do
+ unless spec_file_list.empty?
+ # ruby [ruby_opts] -Ilib -S rcov [rcov_opts] bin/spec -- examples [spec_opts]
+ # or
+ # ruby [ruby_opts] -Ilib bin/spec examples [spec_opts]
+ cmd = "ruby "
+
+ rb_opts = ruby_opts.clone
+ rb_opts << "-I\"#{lib_path}\""
+ rb_opts << "-S rcov" if rcov
+ rb_opts << "-w" if warning
+ cmd << rb_opts.join(" ")
+ cmd << " "
+ cmd << rcov_option_list
+ cmd << %[ -o "#{rcov_dir}" ] if rcov
+ cmd << %Q|"#{spec_script}"|
+ cmd << " "
+ cmd << "-- " if rcov
+ cmd << spec_file_list.collect { |fn| %["#{fn}"] }.join(' ')
+ cmd << " "
+ cmd << spec_option_list
+ if out
+ cmd << " "
+ cmd << %Q| > "#{out}"|
+ STDERR.puts "The Spec::Rake::SpecTask#out attribute is DEPRECATED and will be removed in a future version. Use --format FORMAT:WHERE instead."
+ end
+ unless system(cmd)
+ STDERR.puts failure_message if failure_message
+ raise("Command #{cmd} failed") if fail_on_error
+ end
+ end
+ end
+ end
+
+ if rcov
+ desc "Remove rcov products for #{actual_name}"
+ task paste("clobber_", actual_name) do
+ rm_r rcov_dir rescue nil
+ end
+
+ clobber_task = paste("clobber_", actual_name)
+ task :clobber => [clobber_task]
+
+ task actual_name => clobber_task
+ end
+ self
+ end
+
+ def rcov_option_list # :nodoc:
+ return "" unless rcov
+ ENV['RCOV_OPTS'] || rcov_opts.join(" ") || ""
+ end
+
+ def spec_option_list # :nodoc:
+ STDERR.puts "RSPECOPTS is DEPRECATED and will be removed in a future version. Use SPEC_OPTS instead." if ENV['RSPECOPTS']
+ ENV['SPEC_OPTS'] || ENV['RSPECOPTS'] || spec_opts.join(" ") || ""
+ end
+
+ def evaluate(o) # :nodoc:
+ case o
+ when Proc then o.call
+ else o
+ end
+ end
+
+ def spec_file_list # :nodoc:
+ if ENV['SPEC']
+ FileList[ ENV['SPEC'] ]
+ else
+ result = []
+ result += spec_files.to_a if spec_files
+ result += FileList[ pattern ].to_a if pattern
+ FileList[result]
+ end
+ end
+
+ end
+ end
+end
+
diff --git a/test/lib/spec/rake/verify_rcov.rb b/spec/lib/spec/rake/verify_rcov.rb
index a05153e99..9715744e9 100644
--- a/test/lib/spec/rake/verify_rcov.rb
+++ b/spec/lib/spec/rake/verify_rcov.rb
@@ -19,10 +19,14 @@ module RCov
# exception.
attr_accessor :threshold
+ # Require the threshold value be met exactly. This is the default.
+ attr_accessor :require_exact_threshold
+
def initialize(name=:verify_rcov)
@name = name
@index_html = 'coverage/index.html'
@verbose = true
+ @require_exact_threshold = true
yield self if block_given?
raise "Threshold must be set" if @threshold.nil?
define
@@ -32,6 +36,7 @@ module RCov
desc "Verify that rcov coverage is at least #{threshold}%"
task @name do
total_coverage = nil
+
File.open(index_html).each_line do |line|
if line =~ /<tt.*>(\d+\.\d+)%<\/tt>&nbsp;<\/td>/
total_coverage = eval($1)
@@ -40,8 +45,8 @@ module RCov
end
puts "Coverage: #{total_coverage}% (threshold: #{threshold}%)" if verbose
raise "Coverage must be at least #{threshold}% but was #{total_coverage}%" if total_coverage < threshold
- raise "Coverage has increased above the threshold of #{threshold}% to #{total_coverage}%. You should update your threshold value." if total_coverage > threshold
+ raise "Coverage has increased above the threshold of #{threshold}% to #{total_coverage}%. You should update your threshold value." if (total_coverage > threshold) and require_exact_threshold
end
end
end
-end \ No newline at end of file
+end
diff --git a/spec/lib/spec/runner.rb b/spec/lib/spec/runner.rb
new file mode 100644
index 000000000..9d801adc3
--- /dev/null
+++ b/spec/lib/spec/runner.rb
@@ -0,0 +1,165 @@
+require 'spec/runner/formatter'
+require 'spec/runner/behaviour_runner'
+require 'spec/runner/options'
+require 'spec/runner/option_parser'
+require 'spec/runner/command_line'
+require 'spec/runner/drb_command_line'
+require 'spec/runner/backtrace_tweaker'
+require 'spec/runner/reporter'
+require 'spec/runner/extensions/object'
+require 'spec/runner/extensions/kernel'
+require 'spec/runner/spec_parser'
+
+module Spec
+ # == Behaviours and Examples
+ #
+ # Rather than expressing examples in classes, RSpec uses a custom domain specific language to
+ # describe Behaviours and Examples of those behaviours.
+ #
+ # A Behaviour is the equivalent of a fixture in xUnit-speak. It is a metaphor for the context
+ # in which you will run your executable example - a set of known objects in a known starting state.
+ # We begin be describing
+ #
+ # describe Account do
+ #
+ # before do
+ # @account = Account.new
+ # end
+ #
+ # it "should have a balance of $0" do
+ # @account.balance.should == Money.new(0, :dollars)
+ # end
+ #
+ # end
+ #
+ # We use the before block to set up the Behaviour (given), and then the #it method to
+ # hold the example code that expresses the event (when) and the expected outcome (then).
+ #
+ # == Helper Methods
+ #
+ # A primary goal of RSpec is to keep the examples clear. We therefore prefer
+ # less indirection than you might see in xUnit examples and in well factored, DRY production code. We feel
+ # that duplication is OK if removing it makes it harder to understand an example without
+ # having to look elsewhere to understand its context.
+ #
+ # That said, RSpec does support some level of encapsulating common code in helper
+ # methods that can exist within a context or within an included module.
+ #
+ # == Setup and Teardown
+ #
+ # You can use before and after within a Behaviour. Both methods take an optional
+ # scope argument so you can run the block before :each example or before :all examples
+ #
+ # describe "..." do
+ # before :all do
+ # ...
+ # end
+ #
+ # before :each do
+ # ...
+ # end
+ #
+ # it "should do something" do
+ # ...
+ # end
+ #
+ # it "should do something else" do
+ # ...
+ # end
+ #
+ # after :each do
+ # ...
+ # end
+ #
+ # after :all do
+ # ...
+ # end
+ #
+ # end
+ #
+ # The <tt>before :each</tt> block will run before each of the examples, once for each example. Likewise,
+ # the <tt>after :each</tt> block will run after each of the examples.
+ #
+ # It is also possible to specify a <tt>before :all</tt> and <tt>after :all</tt>
+ # block that will run only once for each behaviour, respectively before the first <code>before :each</code>
+ # and after the last <code>after :each</code>. The use of these is generally discouraged, because it
+ # introduces dependencies between the examples. Still, it might prove useful for very expensive operations
+ # if you know what you are doing.
+ #
+ # == Local helper methods
+ #
+ # You can include local helper methods by simply expressing them within a context:
+ #
+ # describe "..." do
+ #
+ # it "..." do
+ # helper_method
+ # end
+ #
+ # def helper_method
+ # ...
+ # end
+ #
+ # end
+ #
+ # == Included helper methods
+ #
+ # You can include helper methods in multiple contexts by expressing them within
+ # a module, and then including that module in your context:
+ #
+ # module AccountExampleHelperMethods
+ # def helper_method
+ # ...
+ # end
+ # end
+ #
+ # describe "A new account" do
+ # include AccountExampleHelperMethods
+ # before do
+ # @account = Account.new
+ # end
+ #
+ # it "should have a balance of $0" do
+ # helper_method
+ # @account.balance.should eql(Money.new(0, :dollars))
+ # end
+ # end
+ #
+ # == Shared behaviour
+ #
+ # You can define a shared behaviour, that may be used on other behaviours
+ #
+ # describe "All Editions", :shared => true do
+ # it "all editions behaviour" ...
+ # end
+ #
+ # describe SmallEdition do
+ # it_should_behave_like "All Editions"
+ #
+ # it "should do small edition stuff" do
+ # ...
+ # end
+ # end
+ module Runner
+ class << self
+ def configuration # :nodoc:
+ @configuration ||= Spec::DSL::Configuration.new
+ end
+
+ # Use this to configure various configurable aspects of
+ # RSpec:
+ #
+ # Spec::Runner.configure do |configuration|
+ # # Configure RSpec here
+ # end
+ #
+ # The yielded <tt>configuration</tt> object is a
+ # Spec::DSL::Configuration instance. See its RDoc
+ # for details about what you can do with it.
+ #
+ def configure
+ yield configuration if @configuration.nil?
+ end
+ end
+ end
+end
diff --git a/test/lib/spec/runner/backtrace_tweaker.rb b/spec/lib/spec/runner/backtrace_tweaker.rb
index 7300b36b8..aacc2c8b8 100644
--- a/test/lib/spec/runner/backtrace_tweaker.rb
+++ b/spec/lib/spec/runner/backtrace_tweaker.rb
@@ -32,7 +32,9 @@ module Spec
# TextMate's Ruby and RSpec plugins
/Ruby\.tmbundle\/Support\/tmruby.rb:/,
/RSpec\.tmbundle\/Support\/lib/,
- /temp_textmate\./
+ /temp_textmate\./,
+ /mock_frameworks\/rspec/,
+ /spec_server/
]
end
@@ -52,4 +54,4 @@ module Spec
end
end
end
-end \ No newline at end of file
+end
diff --git a/spec/lib/spec/runner/behaviour_runner.rb b/spec/lib/spec/runner/behaviour_runner.rb
new file mode 100644
index 000000000..1ac891f3c
--- /dev/null
+++ b/spec/lib/spec/runner/behaviour_runner.rb
@@ -0,0 +1,123 @@
+module Spec
+ module Runner
+ class BehaviourRunner
+
+ def initialize(options, arg=nil)
+ @behaviours = []
+ @options = options
+ end
+
+ def add_behaviour(behaviour)
+ if !specified_examples.nil? && !specified_examples.empty?
+ behaviour.retain_examples_matching!(specified_examples)
+ end
+ @behaviours << behaviour if behaviour.number_of_examples != 0 && !behaviour.shared?
+ end
+
+ # Runs all behaviours and returns the number of failures.
+ def run(paths, exit_when_done)
+ prepare!(paths)
+ begin
+ run_behaviours
+ rescue Interrupt
+ ensure
+ report_end
+ end
+ failure_count = report_dump
+
+ heckle if(failure_count == 0 && !@options.heckle_runner.nil?)
+
+ if(exit_when_done)
+ exit_code = (failure_count == 0) ? 0 : 1
+ exit(exit_code)
+ end
+ failure_count
+ end
+
+ def report_end
+ @options.reporter.end
+ end
+
+ def report_dump
+ @options.reporter.dump
+ end
+
+ def prepare!(paths)
+ unless paths.nil? # It's nil when running single specs with ruby
+ paths = find_paths(paths)
+ sorted_paths = sort_paths(paths)
+ load_specs(sorted_paths) # This will populate @behaviours via callbacks to add_behaviour
+ end
+ @options.reporter.start(number_of_examples)
+ @behaviours.reverse! if @options.reverse
+ set_sequence_numbers
+ end
+
+ def run_behaviours
+ @behaviours.each do |behaviour|
+ behaviour.run(@options.reporter, @options.dry_run, @options.reverse, @options.timeout)
+ end
+ end
+
+ def number_of_examples
+ @behaviours.inject(0) {|sum, behaviour| sum + behaviour.number_of_examples}
+ end
+
+ FILE_SORTERS = {
+ 'mtime' => lambda {|file_a, file_b| File.mtime(file_b) <=> File.mtime(file_a)}
+ }
+
+ def sorter(paths)
+ FILE_SORTERS[@options.loadby]
+ end
+
+ def sort_paths(paths)
+ sorter = sorter(paths)
+ paths = paths.sort(&sorter) unless sorter.nil?
+ paths
+ end
+
+ private
+
+ # Sets the #number on each Example
+ def set_sequence_numbers
+ number = 0
+ @behaviours.each do |behaviour|
+ number = behaviour.set_sequence_numbers(number, @options.reverse)
+ end
+ end
+
+ def find_paths(paths)
+ result = []
+ paths.each do |path|
+ if File.directory?(path)
+ result += Dir["#{path}/**/*.rb"]
+ elsif File.file?(path)
+ result << path
+ else
+ raise "File or directory not found: #{path}"
+ end
+ end
+ result
+ end
+
+ def load_specs(paths)
+ paths.each do |path|
+ load path
+ end
+ end
+
+ def specified_examples
+ @options.examples
+ end
+
+ def heckle
+ heckle_runner = @options.heckle_runner
+ @options.heckle_runner = nil
+ behaviour_runner = self.class.new(@options)
+ behaviour_runner.instance_variable_set(:@behaviours, @behaviours)
+ heckle_runner.heckle_with(behaviour_runner)
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/runner/command_line.rb b/spec/lib/spec/runner/command_line.rb
new file mode 100644
index 000000000..0d70337e1
--- /dev/null
+++ b/spec/lib/spec/runner/command_line.rb
@@ -0,0 +1,22 @@
+require 'spec/runner/option_parser'
+
+module Spec
+ module Runner
+ # Facade to run specs without having to fork a new ruby process (using `spec ...`)
+ class CommandLine
+ # Runs specs. +argv+ is the commandline args as per the spec commandline API, +err+
+ # and +out+ are the streams output will be written to. +exit+ tells whether or
+ # not a system exit should be called after the specs are run and
+ # +warn_if_no_files+ tells whether or not a warning (the help message)
+ # should be printed to +err+ in case no files are specified.
+ def self.run(argv, err, out, exit=true, warn_if_no_files=true)
+ old_behaviour_runner = defined?($behaviour_runner) ? $behaviour_runner : nil
+ $behaviour_runner = OptionParser.new.create_behaviour_runner(argv, err, out, warn_if_no_files)
+ return if $behaviour_runner.nil? # This is the case if we use --drb
+
+ $behaviour_runner.run(argv, exit)
+ $behaviour_runner = old_behaviour_runner
+ end
+ end
+ end
+end
diff --git a/test/lib/spec/runner/drb_command_line.rb b/spec/lib/spec/runner/drb_command_line.rb
index d4c7d937d..7e745fb71 100644
--- a/test/lib/spec/runner/drb_command_line.rb
+++ b/spec/lib/spec/runner/drb_command_line.rb
@@ -9,8 +9,8 @@ module Spec
def self.run(argv, stderr, stdout, exit=true, warn_if_no_files=true)
begin
DRb.start_service
- rails_spec_server = DRbObject.new_with_uri("druby://localhost:8989")
- rails_spec_server.run(argv, stderr, stdout)
+ spec_server = DRbObject.new_with_uri("druby://localhost:8989")
+ spec_server.run(argv, stderr, stdout)
rescue DRb::DRbConnError
stderr.puts "No server is running"
exit 1 if exit
@@ -18,4 +18,4 @@ module Spec
end
end
end
-end \ No newline at end of file
+end
diff --git a/spec/lib/spec/runner/extensions/kernel.rb b/spec/lib/spec/runner/extensions/kernel.rb
new file mode 100644
index 000000000..75f2c335e
--- /dev/null
+++ b/spec/lib/spec/runner/extensions/kernel.rb
@@ -0,0 +1,50 @@
+module Kernel
+ # Creates and registers an instance of a Spec::DSL::Behaviour (or a subclass).
+ # The instantiated behaviour class depends on the directory of the file
+ # calling this method. For example, Spec::Rails will use different
+ # classes for specs living in <tt>spec/models</tt>, <tt>spec/helpers</tt>,
+ # <tt>spec/views</tt> and <tt>spec/controllers</tt>.
+ #
+ # It is also possible to override autodiscovery of the behaviour class
+ # with an options Hash as the last argument:
+ #
+ # describe "name", :behaviour_type => :something_special do ...
+ #
+ # The reason for using different behaviour classes is to have
+ # different matcher methods available from within the <tt>describe</tt>
+ # block.
+ #
+ # See Spec::DSL::BehaviourFactory#add_behaviour_class for details about
+ # how to register special Spec::DSL::Behaviour implementations.
+ #
+ def describe(*args, &block)
+ raise ArgumentError if args.empty?
+ args << {} unless Hash === args.last
+ args.last[:spec_path] = caller(0)[1]
+ register_behaviour(Spec::DSL::BehaviourFactory.create(*args, &block))
+ end
+ alias :context :describe
+
+ def respond_to(*names)
+ Spec::Matchers::RespondTo.new(*names)
+ end
+
+private
+
+ def register_behaviour(behaviour)
+ if behaviour.shared?
+ Spec::DSL::Behaviour.add_shared_behaviour(behaviour)
+ else
+ behaviour_runner.add_behaviour(behaviour)
+ end
+ end
+
+ def behaviour_runner
+ # TODO: Figure out a better way to get this considered "covered" and keep this statement on multiple lines
+ unless $behaviour_runner; \
+ $behaviour_runner = ::Spec::Runner::OptionParser.new.create_behaviour_runner(ARGV.dup, STDERR, STDOUT, false); \
+ at_exit { $behaviour_runner.run(nil, false) }; \
+ end
+ $behaviour_runner
+ end
+end
diff --git a/test/lib/spec/runner/extensions/object.rb b/spec/lib/spec/runner/extensions/object.rb
index 49745352f..49745352f 100644
--- a/test/lib/spec/runner/extensions/object.rb
+++ b/spec/lib/spec/runner/extensions/object.rb
diff --git a/test/lib/spec/runner/formatter.rb b/spec/lib/spec/runner/formatter.rb
index f62e81733..17512d958 100644
--- a/test/lib/spec/runner/formatter.rb
+++ b/spec/lib/spec/runner/formatter.rb
@@ -1,5 +1,9 @@
+require 'spec/runner/formatter/base_formatter'
require 'spec/runner/formatter/base_text_formatter'
require 'spec/runner/formatter/progress_bar_formatter'
require 'spec/runner/formatter/rdoc_formatter'
require 'spec/runner/formatter/specdoc_formatter'
require 'spec/runner/formatter/html_formatter'
+require 'spec/runner/formatter/failing_examples_formatter'
+require 'spec/runner/formatter/failing_behaviours_formatter'
+require 'spec/runner/formatter/snippet_extractor'
diff --git a/spec/lib/spec/runner/formatter/base_formatter.rb b/spec/lib/spec/runner/formatter/base_formatter.rb
new file mode 100644
index 000000000..7cc43ef0e
--- /dev/null
+++ b/spec/lib/spec/runner/formatter/base_formatter.rb
@@ -0,0 +1,76 @@
+module Spec
+ module Runner
+ module Formatter
+ # Baseclass for formatters that implements all required methods as no-ops.
+ class BaseFormatter
+ def initialize(where)
+ @where = where
+ end
+
+ # This method is invoked before any examples are run, right after
+ # they have all been collected. This can be useful for special
+ # formatters that need to provide progress on feedback (graphical ones)
+ #
+ # This method will only be invoked once, and the next one to be invoked
+ # is #add_behaviour
+ def start(example_count)
+ end
+
+ # This method is invoked at the beginning of the execution of each behaviour.
+ # +name+ is the name of the behaviour and +first+ is true if it is the
+ # first behaviour - otherwise it's false.
+ #
+ # The next method to be invoked after this is #example_failed or #example_finished
+ def add_behaviour(name)
+ end
+
+ # This method is invoked when an +example+ starts.
+ def example_started(example)
+ end
+
+ # This method is invoked when an +example+ passes.
+ def example_passed(example)
+ end
+
+ # This method is invoked when an +example+ fails, i.e. an exception occurred
+ # inside it (such as a failed should or other exception). +counter+ is the
+ # sequence number of the failure (starting at 1) and +failure+ is the associated
+ # Failure object.
+ def example_failed(example, counter, failure)
+ end
+
+ # This method is invoked when an example is not yet implemented (i.e. has not
+ # been provided a block), or when an ExamplePendingError is raised.
+ # +name+ is the name of the example.
+ # +message+ is the message from the ExamplePendingError, if it exists, or the
+ # default value of "Not Yet Implemented"
+ def example_pending(behaviour_name, example_name, message)
+ end
+
+ # This method is invoked after all of the examples have executed. The next method
+ # to be invoked after this one is #dump_failure (once for each failed example),
+ def start_dump
+ end
+
+ # Dumps detailed information about an example failure.
+ # This method is invoked for each failed example after all examples have run. +counter+ is the sequence number
+ # of the associated example. +failure+ is a Failure object, which contains detailed
+ # information about the failure.
+ def dump_failure(counter, failure)
+ end
+
+ # This method is invoked after the dumping of examples and failures.
+ def dump_summary(duration, example_count, failure_count, pending_count)
+ end
+
+ # This gets invoked after the summary if option is set to do so.
+ def dump_pending
+ end
+
+ # This method is invoked at the very end. Allows the formatter to clean up, like closing open streams.
+ def close
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/runner/formatter/base_text_formatter.rb b/spec/lib/spec/runner/formatter/base_text_formatter.rb
new file mode 100644
index 000000000..c3cf01b76
--- /dev/null
+++ b/spec/lib/spec/runner/formatter/base_text_formatter.rb
@@ -0,0 +1,130 @@
+module Spec
+ module Runner
+ module Formatter
+ # Baseclass for text-based formatters. Can in fact be used for
+ # non-text based ones too - just ignore the +output+ constructor
+ # argument.
+ class BaseTextFormatter < BaseFormatter
+ attr_writer :dry_run
+
+ # Creates a new instance that will write to +where+. If +where+ is a
+ # String, output will be written to the File with that name, otherwise
+ # +where+ is exected to be an IO (or an object that responds to #puts and #write).
+ def initialize(where)
+ super(where)
+ if where.is_a?(String)
+ @output = File.open(where, 'w')
+ elsif where == STDOUT
+ @output = Kernel
+ def @output.flush
+ STDOUT.flush
+ end
+ else
+ @output = where
+ end
+ @colour = false
+ @dry_run = false
+ @snippet_extractor = SnippetExtractor.new
+ @pending_examples = []
+ end
+
+ def example_pending(behaviour_name, example_name, message)
+ @pending_examples << ["#{behaviour_name} #{example_name}", message]
+ end
+
+ def colour=(colour)
+ @colour = colour
+ begin ; require 'Win32/Console/ANSI' if @colour && PLATFORM =~ /win32/ ; rescue LoadError ; raise "You must gem install win32console to use colour on Windows" ; end
+ end
+
+ def dump_failure(counter, failure)
+ @output.puts
+ @output.puts "#{counter.to_s})"
+ @output.puts colourise("#{failure.header}\n#{failure.exception.message}", failure)
+ @output.puts format_backtrace(failure.exception.backtrace)
+ @output.flush
+ end
+
+ def colourise(s, failure)
+ if(failure.expectation_not_met?)
+ red(s)
+ elsif(failure.pending_fixed?)
+ blue(s)
+ else
+ magenta(s)
+ end
+ end
+
+ def dump_summary(duration, example_count, failure_count, pending_count)
+ return if @dry_run
+ @output.puts
+ @output.puts "Finished in #{duration} seconds"
+ @output.puts
+
+ summary = "#{example_count} example#{'s' unless example_count == 1}, #{failure_count} failure#{'s' unless failure_count == 1}"
+ summary << ", #{pending_count} pending" if pending_count > 0
+
+ if failure_count == 0
+ if pending_count > 0
+ @output.puts yellow(summary)
+ else
+ @output.puts green(summary)
+ end
+ else
+ @output.puts red(summary)
+ end
+ @output.flush
+ dump_pending
+ end
+
+ def dump_pending
+ unless @pending_examples.empty?
+ @output.puts
+ @output.puts "Pending:"
+ @pending_examples.each do |pending_example|
+ @output.puts "#{pending_example[0]} (#{pending_example[1]})"
+ end
+ end
+ @output.flush
+ end
+
+ def close
+ if IO === @output
+ @output.close
+ end
+ end
+
+ def format_backtrace(backtrace)
+ return "" if backtrace.nil?
+ backtrace.map { |line| backtrace_line(line) }.join("\n")
+ end
+
+ protected
+
+ def backtrace_line(line)
+ line.sub(/\A([^:]+:\d+)$/, '\\1:')
+ end
+
+ def colour(text, colour_code)
+ return text unless @colour && output_to_tty?
+ "#{colour_code}#{text}\e[0m"
+ end
+
+ def output_to_tty?
+ begin
+ @output == Kernel || @output.tty?
+ rescue NoMethodError
+ false
+ end
+ end
+
+ def green(text); colour(text, "\e[32m"); end
+ def red(text); colour(text, "\e[31m"); end
+ def magenta(text); colour(text, "\e[35m"); end
+ def yellow(text); colour(text, "\e[33m"); end
+ def blue(text); colour(text, "\e[34m"); end
+
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/runner/formatter/failing_behaviours_formatter.rb b/spec/lib/spec/runner/formatter/failing_behaviours_formatter.rb
new file mode 100644
index 000000000..2b3940fd3
--- /dev/null
+++ b/spec/lib/spec/runner/formatter/failing_behaviours_formatter.rb
@@ -0,0 +1,29 @@
+module Spec
+ module Runner
+ module Formatter
+ class FailingBehavioursFormatter < BaseTextFormatter
+ def add_behaviour(behaviour_name)
+ if behaviour_name =~ /(.*) \(druby.*\)$/
+ @behaviour_name = $1
+ else
+ @behaviour_name = behaviour_name
+ end
+ end
+
+ def example_failed(example, counter, failure)
+ unless @behaviour_name.nil?
+ @output.puts @behaviour_name
+ @behaviour_name = nil
+ @output.flush
+ end
+ end
+
+ def dump_failure(counter, failure)
+ end
+
+ def dump_summary(duration, example_count, failure_count, pending_count)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/runner/formatter/failing_examples_formatter.rb b/spec/lib/spec/runner/formatter/failing_examples_formatter.rb
new file mode 100644
index 000000000..9728deaf0
--- /dev/null
+++ b/spec/lib/spec/runner/formatter/failing_examples_formatter.rb
@@ -0,0 +1,22 @@
+module Spec
+ module Runner
+ module Formatter
+ class FailingExamplesFormatter < BaseTextFormatter
+ def add_behaviour(behaviour_name)
+ @behaviour_name = behaviour_name
+ end
+
+ def example_failed(example, counter, failure)
+ @output.puts "#{@behaviour_name} #{example.description}"
+ @output.flush
+ end
+
+ def dump_failure(counter, failure)
+ end
+
+ def dump_summary(duration, example_count, failure_count, pending_count)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/runner/formatter/html_formatter.rb b/spec/lib/spec/runner/formatter/html_formatter.rb
new file mode 100644
index 000000000..d9c422e55
--- /dev/null
+++ b/spec/lib/spec/runner/formatter/html_formatter.rb
@@ -0,0 +1,323 @@
+require 'erb'
+
+module Spec
+ module Runner
+ module Formatter
+ class HtmlFormatter < BaseTextFormatter
+ include ERB::Util # for the #h method
+
+ def initialize(output)
+ super
+ @current_behaviour_number = 0
+ @current_example_number = 0
+ end
+
+ # The number of the currently running behaviour
+ def current_behaviour_number
+ @current_behaviour_number
+ end
+
+ # The number of the currently running example (a global counter)
+ def current_example_number
+ @current_example_number
+ end
+
+ def start(example_count)
+ @example_count = example_count
+
+ @output.puts html_header
+ @output.puts report_header
+ @output.flush
+ end
+
+ def add_behaviour(name)
+ @behaviour_red = false
+ @behaviour_red = false
+ @current_behaviour_number += 1
+ unless current_behaviour_number == 1
+ @output.puts " </dl>"
+ @output.puts "</div>"
+ end
+ @output.puts "<div class=\"behaviour\">"
+ @output.puts " <dl>"
+ @output.puts " <dt id=\"behaviour_#{current_behaviour_number}\">#{h(name)}</dt>"
+ @output.flush
+ end
+
+ def start_dump
+ @output.puts " </dl>"
+ @output.puts "</div>"
+ @output.flush
+ end
+
+ def example_started(example)
+ @current_example_number = example.number
+ end
+
+ def example_passed(example)
+ move_progress
+ @output.puts " <dd class=\"spec passed\"><span class=\"passed_spec_name\">#{h(example.description)}</span></dd>"
+ @output.flush
+ end
+
+ def example_failed(example, counter, failure)
+ extra = extra_failure_content(failure)
+ failure_style = failure.pending_fixed? ? 'pending_fixed' : 'failed'
+ @output.puts " <script type=\"text/javascript\">makeRed('rspec-header');</script>" unless @header_red
+ @header_red = true
+ @output.puts " <script type=\"text/javascript\">makeRed('behaviour_#{current_behaviour_number}');</script>" unless @behaviour_red
+ @behaviour_red = true
+ move_progress
+ @output.puts " <dd class=\"spec #{failure_style}\">"
+ @output.puts " <span class=\"failed_spec_name\">#{h(example.description)}</span>"
+ @output.puts " <div class=\"failure\" id=\"failure_#{counter}\">"
+ @output.puts " <div class=\"message\"><pre>#{h(failure.exception.message)}</pre></div>" unless failure.exception.nil?
+ @output.puts " <div class=\"backtrace\"><pre>#{format_backtrace(failure.exception.backtrace)}</pre></div>" unless failure.exception.nil?
+ @output.puts extra unless extra == ""
+ @output.puts " </div>"
+ @output.puts " </dd>"
+ @output.flush
+ end
+
+ def example_pending(behaviour_name, example_name, message)
+ @output.puts " <script type=\"text/javascript\">makeYellow('rspec-header');</script>" unless @header_red
+ @output.puts " <script type=\"text/javascript\">makeYellow('behaviour_#{current_behaviour_number}');</script>" unless @behaviour_red
+ move_progress
+ @output.puts " <dd class=\"spec not_implemented\"><span class=\"not_implemented_spec_name\">#{h(example_name)}</span></dd>"
+ @output.flush
+ end
+
+ # Override this method if you wish to output extra HTML for a failed spec. For example, you
+ # could output links to images or other files produced during the specs.
+ #
+ def extra_failure_content(failure)
+ " <pre class=\"ruby\"><code>#{@snippet_extractor.snippet(failure.exception)}</code></pre>"
+ end
+
+ def move_progress
+ percent_done = @example_count == 0 ? 100.0 : ((current_example_number + 1).to_f / @example_count.to_f * 1000).to_i / 10.0
+ @output.puts " <script type=\"text/javascript\">moveProgressBar('#{percent_done}');</script>"
+ @output.flush
+ end
+
+ def dump_failure(counter, failure)
+ end
+
+ def dump_summary(duration, example_count, failure_count, pending_count)
+ if @dry_run
+ totals = "This was a dry-run"
+ else
+ totals = "#{example_count} example#{'s' unless example_count == 1}, #{failure_count} failure#{'s' unless failure_count == 1}"
+ totals << ", #{pending_count} pending" if pending_count > 0
+ end
+ @output.puts "<script type=\"text/javascript\">document.getElementById('duration').innerHTML = \"Finished in <strong>#{duration} seconds</strong>\";</script>"
+ @output.puts "<script type=\"text/javascript\">document.getElementById('totals').innerHTML = \"#{totals}\";</script>"
+ @output.puts "</div>"
+ @output.puts "</div>"
+ @output.puts "</body>"
+ @output.puts "</html>"
+ @output.flush
+ end
+
+ def html_header
+ <<-EOF
+<?xml version="1.0" encoding="iso-8859-1"?>
+<!DOCTYPE html
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
+
+<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
+<head>
+ <title>RSpec results</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
+ <meta http-equiv="Expires" content="-1" />
+ <meta http-equiv="Pragma" content="no-cache" />
+ <style type="text/css">
+ body {
+ margin: 0;
+ padding: 0;
+ background: #fff;
+ font-size: 80%;
+ }
+ </style>
+</head>
+<body>
+EOF
+ end
+
+ def report_header
+ <<-EOF
+<div class="rspec-report">
+ <script type="text/javascript">
+ // <![CDATA[
+#{global_scripts}
+ // ]]>
+ </script>
+ <style type="text/css">
+#{global_styles}
+ </style>
+
+<div id="rspec-header">
+ <h1>RSpec Results</h1>
+
+ <div id="summary">
+ <p id="totals">&nbsp;</p>
+ <p id="duration">&nbsp;</p>
+ </div>
+</div>
+
+<div class="results">
+EOF
+ end
+
+ def global_scripts
+ <<-EOF
+function moveProgressBar(percentDone) {
+ document.getElementById("rspec-header").style.width = percentDone +"%";
+}
+function makeRed(element_id) {
+ document.getElementById(element_id).style.background = '#C40D0D';
+ document.getElementById(element_id).style.color = '#FFFFFF';
+}
+
+function makeYellow(element_id) {
+ if (element_id == "rspec-header" && document.getElementById(element_id).style.background != '#C40D0D')
+ {
+ document.getElementById(element_id).style.background = '#FAF834';
+ document.getElementById(element_id).style.color = '#000000';
+ }
+ else
+ {
+ document.getElementById(element_id).style.background = '#FAF834';
+ document.getElementById(element_id).style.color = '#000000';
+ }
+}
+EOF
+ end
+
+ def global_styles
+ <<-EOF
+#rspec-header {
+ background: #65C400; color: #fff;
+}
+
+.rspec-report h1 {
+ margin: 0px 10px 0px 10px;
+ padding: 10px;
+ font-family: "Lucida Grande", Helvetica, sans-serif;
+ font-size: 1.8em;
+}
+
+#summary {
+ margin: 0; padding: 5px 10px;
+ font-family: "Lucida Grande", Helvetica, sans-serif;
+ text-align: right;
+ position: absolute;
+ top: 0px;
+ right: 0px;
+}
+
+#summary p {
+ margin: 0 0 0 2px;
+}
+
+#summary #totals {
+ font-size: 1.2em;
+}
+
+.behaviour {
+ margin: 0 10px 5px;
+ background: #fff;
+}
+
+dl {
+ margin: 0; padding: 0 0 5px;
+ font: normal 11px "Lucida Grande", Helvetica, sans-serif;
+}
+
+dt {
+ padding: 3px;
+ background: #65C400;
+ color: #fff;
+ font-weight: bold;
+}
+
+dd {
+ margin: 5px 0 5px 5px;
+ padding: 3px 3px 3px 18px;
+}
+
+dd.spec.passed {
+ border-left: 5px solid #65C400;
+ border-bottom: 1px solid #65C400;
+ background: #DBFFB4; color: #3D7700;
+}
+
+dd.spec.failed {
+ border-left: 5px solid #C20000;
+ border-bottom: 1px solid #C20000;
+ color: #C20000; background: #FFFBD3;
+}
+
+dd.spec.not_implemented {
+ border-left: 5px solid #FAF834;
+ border-bottom: 1px solid #FAF834;
+ background: #FCFB98; color: #131313;
+}
+
+dd.spec.pending_fixed {
+ border-left: 5px solid #0000C2;
+ border-bottom: 1px solid #0000C2;
+ color: #0000C2; background: #D3FBFF;
+}
+
+.backtrace {
+ color: #000;
+ font-size: 12px;
+}
+
+a {
+ color: #BE5C00;
+}
+
+/* Ruby code, style similar to vibrant ink */
+.ruby {
+ font-size: 12px;
+ font-family: monospace;
+ color: white;
+ background-color: black;
+ padding: 0.1em 0 0.2em 0;
+}
+
+.ruby .keyword { color: #FF6600; }
+.ruby .constant { color: #339999; }
+.ruby .attribute { color: white; }
+.ruby .global { color: white; }
+.ruby .module { color: white; }
+.ruby .class { color: white; }
+.ruby .string { color: #66FF00; }
+.ruby .ident { color: white; }
+.ruby .method { color: #FFCC00; }
+.ruby .number { color: white; }
+.ruby .char { color: white; }
+.ruby .comment { color: #9933CC; }
+.ruby .symbol { color: white; }
+.ruby .regex { color: #44B4CC; }
+.ruby .punct { color: white; }
+.ruby .escape { color: white; }
+.ruby .interp { color: white; }
+.ruby .expr { color: white; }
+
+.ruby .offending { background-color: gray; }
+.ruby .linenum {
+ width: 75px;
+ padding: 0.1em 1em 0.2em 0;
+ color: #000000;
+ background-color: #FFFBD3;
+}
+EOF
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/runner/formatter/progress_bar_formatter.rb b/spec/lib/spec/runner/formatter/progress_bar_formatter.rb
new file mode 100644
index 000000000..624f06e7c
--- /dev/null
+++ b/spec/lib/spec/runner/formatter/progress_bar_formatter.rb
@@ -0,0 +1,31 @@
+module Spec
+ module Runner
+ module Formatter
+ class ProgressBarFormatter < BaseTextFormatter
+ def add_behaviour(name)
+ end
+
+ def example_failed(example, counter, failure)
+ @output.print colourise('F', failure)
+ @output.flush
+ end
+
+ def example_passed(example)
+ @output.print green('.')
+ @output.flush
+ end
+
+ def example_pending(behaviour_name, example_name, message)
+ super
+ @output.print yellow('P')
+ @output.flush
+ end
+
+ def start_dump
+ @output.puts
+ @output.flush
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/runner/formatter/rdoc_formatter.rb b/spec/lib/spec/runner/formatter/rdoc_formatter.rb
new file mode 100644
index 000000000..0fd22ba6c
--- /dev/null
+++ b/spec/lib/spec/runner/formatter/rdoc_formatter.rb
@@ -0,0 +1,24 @@
+module Spec
+ module Runner
+ module Formatter
+ class RdocFormatter < BaseTextFormatter
+ def add_behaviour(name)
+ @output.puts "# #{name}"
+ end
+
+ def example_passed(example)
+ @output.puts "# * #{example.description}"
+ @output.flush
+ end
+
+ def example_failed(example, counter, failure)
+ @output.puts "# * #{example.description} [#{counter} - FAILED]"
+ end
+
+ def example_pending(behaviour_name, example_name, message)
+ @output.puts "# * #{behaviour_name} #{example_name} [PENDING: #{message}]"
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/runner/formatter/snippet_extractor.rb b/spec/lib/spec/runner/formatter/snippet_extractor.rb
new file mode 100644
index 000000000..41119fe46
--- /dev/null
+++ b/spec/lib/spec/runner/formatter/snippet_extractor.rb
@@ -0,0 +1,52 @@
+module Spec
+ module Runner
+ module Formatter
+ # This class extracts code snippets by looking at the backtrace of the passed error
+ class SnippetExtractor #:nodoc:
+ class NullConverter; def convert(code, pre); code; end; end #:nodoc:
+ begin; require 'rubygems'; require 'syntax/convertors/html'; @@converter = Syntax::Convertors::HTML.for_syntax "ruby"; rescue LoadError => e; @@converter = NullConverter.new; end
+
+ def snippet(error)
+ raw_code, line = snippet_for(error.backtrace[0])
+ highlighted = @@converter.convert(raw_code, false)
+ highlighted << "\n<span class=\"comment\"># gem install syntax to get syntax highlighting</span>" if @@converter.is_a?(NullConverter)
+ post_process(highlighted, line)
+ end
+
+ def snippet_for(error_line)
+ if error_line =~ /(.*):(\d+)/
+ file = $1
+ line = $2.to_i
+ [lines_around(file, line), line]
+ else
+ ["# Couldn't get snippet for #{error_line}", 1]
+ end
+ end
+
+ def lines_around(file, line)
+ if File.file?(file)
+ lines = File.open(file).read.split("\n")
+ min = [0, line-3].max
+ max = [line+1, lines.length-1].min
+ selected_lines = []
+ selected_lines.join("\n")
+ lines[min..max].join("\n")
+ else
+ "# Couldn't get snippet for #{file}"
+ end
+ end
+
+ def post_process(highlighted, offending_line)
+ new_lines = []
+ highlighted.split("\n").each_with_index do |line, i|
+ new_line = "<span class=\"linenum\">#{offending_line+i-2}</span>#{line}"
+ new_line = "<span class=\"offending\">#{new_line}</span>" if i == 2
+ new_lines << new_line
+ end
+ new_lines.join("\n")
+ end
+
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/runner/formatter/specdoc_formatter.rb b/spec/lib/spec/runner/formatter/specdoc_formatter.rb
new file mode 100644
index 000000000..ad794b238
--- /dev/null
+++ b/spec/lib/spec/runner/formatter/specdoc_formatter.rb
@@ -0,0 +1,29 @@
+module Spec
+ module Runner
+ module Formatter
+ class SpecdocFormatter < BaseTextFormatter
+ def add_behaviour(name)
+ @output.puts
+ @output.puts name
+ @output.flush
+ end
+
+ def example_failed(example, counter, failure)
+ @output.puts failure.expectation_not_met? ? red("- #{example.description} (FAILED - #{counter})") : magenta("- #{example.description} (ERROR - #{counter})")
+ @output.flush
+ end
+
+ def example_passed(example)
+ @output.puts green("- #{example.description}")
+ @output.flush
+ end
+
+ def example_pending(behaviour_name, example_name, message)
+ super
+ @output.puts yellow("- #{example_name} (PENDING: #{message})")
+ @output.flush
+ end
+ end
+ end
+ end
+end
diff --git a/test/lib/spec/runner/heckle_runner.rb b/spec/lib/spec/runner/heckle_runner.rb
index fd36389de..b6de4ef73 100644
--- a/test/lib/spec/runner/heckle_runner.rb
+++ b/spec/lib/spec/runner/heckle_runner.rb
@@ -13,9 +13,9 @@ module Spec
@heckle_class = heckle_class
end
- # Runs all the contexts held by +context_runner+ once for each of the
+ # Runs all the contexts held by +behaviour_runner+ once for each of the
# methods in the matched classes.
- def heckle_with(context_runner)
+ def heckle_with(behaviour_runner)
if @filter =~ /(.*)[#\.](.*)/
heckle_method($1, $2)
else
@@ -25,7 +25,7 @@ module Spec
def heckle_method(class_name, method_name)
verify_constant(class_name)
- heckle = @heckle_class.new(class_name, method_name, context_runner)
+ heckle = @heckle_class.new(class_name, method_name, behaviour_runner)
heckle.validate
end
@@ -39,7 +39,7 @@ module Spec
classes.each do |klass|
klass.instance_methods(false).each do |method_name|
- heckle = @heckle_class.new(klass.name, method_name, context_runner)
+ heckle = @heckle_class.new(klass.name, method_name, behaviour_runner)
heckle.validate
end
end
@@ -57,13 +57,14 @@ module Spec
#Supports Heckle 1.2 and prior (earlier versions used Heckle::Base)
class Heckler < (Heckle.const_defined?(:Base) ? Heckle::Base : Heckle)
- def initialize(klass_name, method_name, context_runner)
+ def initialize(klass_name, method_name, behaviour_runner)
super(klass_name, method_name)
- @context_runner = context_runner
+ @behaviour_runner = behaviour_runner
end
def tests_pass?
- failure_count = @context_runner.run(false)
+ paths = [] # We can pass an empty array of paths - our specs are already loaded.
+ failure_count = @behaviour_runner.run(paths, false)
failure_count == 0
end
end
diff --git a/test/lib/spec/runner/heckle_runner_win.rb b/spec/lib/spec/runner/heckle_runner_unsupported.rb
index 031386599..02aa37953 100644
--- a/test/lib/spec/runner/heckle_runner_win.rb
+++ b/spec/lib/spec/runner/heckle_runner_unsupported.rb
@@ -1,10 +1,10 @@
-module Spec
- module Runner
- # Dummy implementation for Windows that just fails (Heckle is not supported on Windows)
- class HeckleRunner
- def initialize(filter)
- raise "Heckle not supported on Windows"
- end
- end
- end
-end
+module Spec
+ module Runner
+ # Dummy implementation for Windows that just fails (Heckle is not supported on Windows)
+ class HeckleRunner
+ def initialize(filter)
+ raise "Heckle not supported on Windows"
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/runner/option_parser.rb b/spec/lib/spec/runner/option_parser.rb
new file mode 100644
index 000000000..1facb85a8
--- /dev/null
+++ b/spec/lib/spec/runner/option_parser.rb
@@ -0,0 +1,227 @@
+require 'optparse'
+require 'stringio'
+
+module Spec
+ module Runner
+ class OptionParser
+ BUILT_IN_FORMATTERS = {
+ 'specdoc' => Formatter::SpecdocFormatter,
+ 's' => Formatter::SpecdocFormatter,
+ 'html' => Formatter::HtmlFormatter,
+ 'h' => Formatter::HtmlFormatter,
+ 'rdoc' => Formatter::RdocFormatter,
+ 'r' => Formatter::RdocFormatter,
+ 'progress' => Formatter::ProgressBarFormatter,
+ 'p' => Formatter::ProgressBarFormatter,
+ 'failing_examples' => Formatter::FailingExamplesFormatter,
+ 'e' => Formatter::FailingExamplesFormatter,
+ 'failing_behaviours' => Formatter::FailingBehavioursFormatter,
+ 'b' => Formatter::FailingBehavioursFormatter
+ }
+
+ COMMAND_LINE = {
+ :diff => ["-D", "--diff [FORMAT]", "Show diff of objects that are expected to be equal when they are not",
+ "Builtin formats: unified|u|context|c",
+ "You can also specify a custom differ class",
+ "(in which case you should also specify --require)"],
+ :colour => ["-c", "--colour", "--color", "Show coloured (red/green) output"],
+ :example => ["-e", "--example [NAME|FILE_NAME]", "Execute example(s) with matching name(s). If the argument is",
+ "the path to an existing file (typically generated by a previous",
+ "run using --format failing_examples:file.txt), then the examples",
+ "on each line of thatfile will be executed. If the file is empty,",
+ "all examples will be run (as if --example was not specified).",
+ " ",
+ "If the argument is not an existing file, then it is treated as",
+ "an example name directly, causing RSpec to run just the example",
+ "matching that name"],
+ :specification => ["-s", "--specification [NAME]", "DEPRECATED - use -e instead", "(This will be removed when autotest works with -e)"],
+ :line => ["-l", "--line LINE_NUMBER", Integer, "Execute behaviout or specification at given line.",
+ "(does not work for dynamically generated specs)"],
+ :format => ["-f", "--format FORMAT[:WHERE]", "Specifies what format to use for output. Specify WHERE to tell",
+ "the formatter where to write the output. All built-in formats",
+ "expect WHERE to be a file name, and will write to STDOUT if it's",
+ "not specified. The --format option may be specified several times",
+ "if you want several outputs",
+ " ",
+ "Builtin formats: ",
+ "progress|p : Text progress",
+ "specdoc|s : Behaviour doc as text",
+ "rdoc|r : Behaviour doc as RDoc",
+ "html|h : A nice HTML report",
+ "failing_examples|e : Write all failing examples - input for --example",
+ "failing_behaviours|b : Write all failing behaviours - input for --example",
+ " ",
+ "FORMAT can also be the name of a custom formatter class",
+ "(in which case you should also specify --require to load it)"],
+ :require => ["-r", "--require FILE", "Require FILE before running specs",
+ "Useful for loading custom formatters or other extensions.",
+ "If this option is used it must come before the others"],
+ :backtrace => ["-b", "--backtrace", "Output full backtrace"],
+ :loadby => ["-L", "--loadby STRATEGY", "Specify the strategy by which spec files should be loaded.",
+ "STRATEGY can currently only be 'mtime' (File modification time)",
+ "By default, spec files are loaded in alphabetical order if --loadby",
+ "is not specified."],
+ :reverse => ["-R", "--reverse", "Run examples in reverse order"],
+ :timeout => ["-t", "--timeout FLOAT", "Interrupt and fail each example that doesn't complete in the",
+ "specified time"],
+ :heckle => ["-H", "--heckle CODE", "If all examples pass, this will mutate the classes and methods",
+ "identified by CODE little by little and run all the examples again",
+ "for each mutation. The intent is that for each mutation, at least",
+ "one example *should* fail, and RSpec will tell you if this is not the",
+ "case. CODE should be either Some::Module, Some::Class or",
+ "Some::Fabulous#method}"],
+ :dry_run => ["-d", "--dry-run", "Invokes formatters without executing the examples."],
+ :options_file => ["-O", "--options PATH", "Read options from a file"],
+ :generate_options => ["-G", "--generate-options PATH", "Generate an options file for --options"],
+ :runner => ["-U", "--runner RUNNER", "Use a custom BehaviourRunner."],
+ :drb => ["-X", "--drb", "Run examples via DRb. (For example against script/spec_server)"],
+ :version => ["-v", "--version", "Show version"],
+ :help => ["-h", "--help", "You're looking at it"]
+ }
+
+ def initialize
+ @spec_parser = SpecParser.new
+ @file_factory = File
+ end
+
+ def create_behaviour_runner(args, err, out, warn_if_no_files)
+ options = parse(args, err, out, warn_if_no_files)
+ # Some exit points in parse (--generate-options, --drb) don't return the options,
+ # but hand over control. In that case we don't want to continue.
+ return nil unless options.is_a?(Options)
+ options.configure
+ options.behaviour_runner
+ end
+
+ def parse(args, err, out, warn_if_no_files)
+ options_file = nil
+ args_copy = args.dup
+ options = Options.new(err, out)
+
+ opts = ::OptionParser.new do |opts|
+ opts.banner = "Usage: spec (FILE|DIRECTORY|GLOB)+ [options]"
+ opts.separator ""
+
+ def opts.rspec_on(name, &block)
+ on(*COMMAND_LINE[name], &block)
+ end
+
+ opts.rspec_on(:diff) {|diff| options.parse_diff(diff)}
+
+ opts.rspec_on(:colour) {options.colour = true}
+
+ opts.rspec_on(:example) {|example| options.parse_example(example)}
+
+ opts.rspec_on(:specification) {|example| options.parse_example(example)}
+
+ opts.rspec_on(:line) {|line_number| options.line_number = line_number.to_i}
+
+ opts.rspec_on(:format) {|format| options.parse_format(format)}
+
+ opts.rspec_on(:require) {|req| options.parse_require(req)}
+
+ opts.rspec_on(:backtrace) {options.backtrace_tweaker = NoisyBacktraceTweaker.new}
+
+ opts.rspec_on(:loadby) {|loadby| options.loadby = loadby}
+
+ opts.rspec_on(:reverse) {options.reverse = true}
+
+ opts.rspec_on(:timeout) {|timeout| options.timeout = timeout.to_f}
+
+ opts.rspec_on(:heckle) {|heckle| options.parse_heckle(heckle)}
+
+ opts.rspec_on(:dry_run) {options.dry_run = true}
+
+ opts.rspec_on(:options_file) do |options_file|
+ return parse_options_file(options_file, out, err, args_copy, warn_if_no_files)
+ end
+
+ opts.rspec_on(:generate_options) do |options_file|
+ options.parse_generate_options(options_file, args_copy, out)
+ end
+
+ opts.rspec_on(:runner) do |runner|
+ options.runner_arg = runner
+ end
+
+ opts.rspec_on(:drb) do
+ return parse_drb(args_copy, out, err, warn_if_no_files)
+ end
+
+ opts.rspec_on(:version) {parse_version(out)}
+
+ opts.on_tail(*COMMAND_LINE[:help]) {parse_help(opts, out)}
+ end
+ opts.parse!(args)
+
+ if args.empty? && warn_if_no_files
+ err.puts "No files specified."
+ err.puts opts
+ exit(6) if err == $stderr
+ end
+
+ if options.line_number
+ set_spec_from_line_number(options, args, err)
+ end
+
+ if options.formatters.empty?
+ options.formatters << Formatter::ProgressBarFormatter.new(out)
+ end
+
+ options
+ end
+
+ def parse_options_file(options_file, out_stream, error_stream, args_copy, warn_if_no_files)
+ # Remove the --options option and the argument before writing to file
+ index = args_copy.index("-O") || args_copy.index("--options")
+ args_copy.delete_at(index)
+ args_copy.delete_at(index)
+
+ new_args = args_copy + IO.readlines(options_file).map {|l| l.chomp.split " "}.flatten
+ return CommandLine.run(new_args, error_stream, out_stream, true, warn_if_no_files)
+ end
+
+ def parse_drb(args_copy, out_stream, error_stream, warn_if_no_files)
+ # Remove the --drb option
+ index = args_copy.index("-X") || args_copy.index("--drb")
+ args_copy.delete_at(index)
+
+ return DrbCommandLine.run(args_copy, error_stream, out_stream, true, warn_if_no_files)
+ end
+
+ def parse_version(out_stream)
+ out_stream.puts ::Spec::VERSION::DESCRIPTION
+ exit if out_stream == $stdout
+ end
+
+ def parse_help(opts, out_stream)
+ out_stream.puts opts
+ exit if out_stream == $stdout
+ end
+
+ def set_spec_from_line_number(options, args, err)
+ if options.examples.empty?
+ if args.length == 1
+ if @file_factory.file?(args[0])
+ source = @file_factory.open(args[0])
+ example = @spec_parser.spec_name_for(source, options.line_number)
+ options.parse_example(example)
+ elsif @file_factory.directory?(args[0])
+ err.puts "You must specify one file, not a directory when using the --line option"
+ exit(1) if err == $stderr
+ else
+ err.puts "#{args[0]} does not exist"
+ exit(2) if err == $stderr
+ end
+ else
+ err.puts "Only one file can be specified when using the --line option: #{args.inspect}"
+ exit(3) if err == $stderr
+ end
+ else
+ err.puts "You cannot use both --line and --example"
+ exit(4) if err == $stderr
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/runner/options.rb b/spec/lib/spec/runner/options.rb
new file mode 100644
index 000000000..a940133eb
--- /dev/null
+++ b/spec/lib/spec/runner/options.rb
@@ -0,0 +1,175 @@
+module Spec
+ module Runner
+ class Options
+ BUILT_IN_FORMATTERS = {
+ 'specdoc' => Formatter::SpecdocFormatter,
+ 's' => Formatter::SpecdocFormatter,
+ 'html' => Formatter::HtmlFormatter,
+ 'h' => Formatter::HtmlFormatter,
+ 'rdoc' => Formatter::RdocFormatter,
+ 'r' => Formatter::RdocFormatter,
+ 'progress' => Formatter::ProgressBarFormatter,
+ 'p' => Formatter::ProgressBarFormatter,
+ 'failing_examples' => Formatter::FailingExamplesFormatter,
+ 'e' => Formatter::FailingExamplesFormatter,
+ 'failing_behaviours' => Formatter::FailingBehavioursFormatter,
+ 'b' => Formatter::FailingBehavioursFormatter
+ }
+
+ attr_accessor(
+ :backtrace_tweaker,
+ :colour,
+ :context_lines,
+ :diff_format,
+ :differ_class,
+ :dry_run,
+ :examples,
+ :failure_file,
+ :formatters,
+ :generate,
+ :heckle_runner,
+ :line_number,
+ :loadby,
+ :reporter,
+ :reverse,
+ :timeout,
+ :verbose,
+ :runner_arg,
+ :behaviour_runner
+ )
+
+ def initialize(err, out)
+ @err, @out = err, out
+ @backtrace_tweaker = QuietBacktraceTweaker.new
+ @examples = []
+ @formatters = []
+ @colour = false
+ @dry_run = false
+ end
+
+ def configure
+ configure_formatters
+ create_reporter
+ configure_differ
+ create_behaviour_runner
+ end
+
+ def create_behaviour_runner
+ return nil if @generate
+ @behaviour_runner = if @runner_arg
+ klass_name, arg = split_at_colon(@runner_arg)
+ runner_type = load_class(klass_name, 'behaviour runner', '--runner')
+ runner_type.new(self, arg)
+ else
+ BehaviourRunner.new(self)
+ end
+ end
+
+ def configure_formatters
+ @formatters.each do |formatter|
+ formatter.colour = @colour if formatter.respond_to?(:colour=)
+ formatter.dry_run = @dry_run if formatter.respond_to?(:dry_run=)
+ end
+ end
+
+ def create_reporter
+ @reporter = Reporter.new(@formatters, @backtrace_tweaker)
+ end
+
+ def configure_differ
+ if @differ_class
+ Spec::Expectations.differ = @differ_class.new(@diff_format, @context_lines, @colour)
+ end
+ end
+
+ def parse_diff(format)
+ @context_lines = 3
+ case format
+ when :context, 'context', 'c'
+ @diff_format = :context
+ when :unified, 'unified', 'u', '', nil
+ @diff_format = :unified
+ end
+
+ if [:context,:unified].include? @diff_format
+ require 'spec/expectations/differs/default'
+ @differ_class = Spec::Expectations::Differs::Default
+ else
+ @diff_format = :custom
+ @differ_class = load_class(format, 'differ', '--diff')
+ end
+ end
+
+ def parse_example(example)
+ if(File.file?(example))
+ @examples = File.open(example).read.split("\n")
+ else
+ @examples = [example]
+ end
+ end
+
+ def parse_format(format_arg)
+ format, where = split_at_colon(format_arg)
+ # This funky regexp checks whether we have a FILE_NAME or not
+ if where.nil?
+ raise "When using several --format options only one of them can be without a file" if @out_used
+ where = @out
+ @out_used = true
+ end
+
+ formatter_type = BUILT_IN_FORMATTERS[format] || load_class(format, 'formatter', '--format')
+ @formatters << formatter_type.new(where)
+ end
+
+ def parse_require(req)
+ req.split(",").each{|file| require file}
+ end
+
+ def parse_heckle(heckle)
+ heckle_require = [/mswin/, /java/].detect{|p| p =~ RUBY_PLATFORM} ? 'spec/runner/heckle_runner_unsupported' : 'spec/runner/heckle_runner'
+ require heckle_require
+ @heckle_runner = HeckleRunner.new(heckle)
+ end
+
+ def parse_generate_options(options_file, args_copy, out_stream)
+ # Remove the --generate-options option and the argument before writing to file
+ index = args_copy.index("-G") || args_copy.index("--generate-options")
+ args_copy.delete_at(index)
+ args_copy.delete_at(index)
+ File.open(options_file, 'w') do |io|
+ io.puts args_copy.join("\n")
+ end
+ out_stream.puts "\nOptions written to #{options_file}. You can now use these options with:"
+ out_stream.puts "spec --options #{options_file}"
+ @generate = true
+ end
+
+ def split_at_colon(s)
+ if s =~ /([a-zA-Z_]+(?:::[a-zA-Z_]+)*):?(.*)/
+ arg = $2 == "" ? nil : $2
+ [$1, arg]
+ else
+ raise "Couldn't parse #{s.inspect}"
+ end
+ end
+
+ def load_class(name, kind, option)
+ if name =~ /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/
+ arg = $2 == "" ? nil : $2
+ [$1, arg]
+ else
+ m = "#{name.inspect} is not a valid class name"
+ @err.puts m
+ raise m
+ end
+ begin
+ eval(name, binding, __FILE__, __LINE__)
+ rescue NameError => e
+ @err.puts "Couldn't find #{kind} class #{name}"
+ @err.puts "Make sure the --require option is specified *before* #{option}"
+ if $_spec_spec ; raise e ; else exit(1) ; end
+ end
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/runner/reporter.rb b/spec/lib/spec/runner/reporter.rb
new file mode 100644
index 000000000..b1dc2a27a
--- /dev/null
+++ b/spec/lib/spec/runner/reporter.rb
@@ -0,0 +1,125 @@
+module Spec
+ module Runner
+ class Reporter
+
+ def initialize(formatters, backtrace_tweaker)
+ @formatters = formatters
+ @backtrace_tweaker = backtrace_tweaker
+ clear!
+ end
+
+ def add_behaviour(name)
+ @formatters.each{|f| f.add_behaviour(name)}
+ @behaviour_names << name
+ end
+
+ def example_started(name)
+ @formatters.each{|f| f.example_started(name)}
+ end
+
+ def example_finished(name, error=nil, failure_location=nil, not_implemented = false)
+ @example_names << name
+
+ if not_implemented
+ example_pending(@behaviour_names.last, name)
+ elsif error.nil?
+ example_passed(name)
+ elsif Spec::DSL::ExamplePendingError === error
+ example_pending(@behaviour_names.last, name, error.message)
+ else
+ example_failed(name, error, failure_location)
+ end
+ end
+
+ def start(number_of_examples)
+ clear!
+ @start_time = Time.new
+ @formatters.each{|f| f.start(number_of_examples)}
+ end
+
+ def end
+ @end_time = Time.new
+ end
+
+ # Dumps the summary and returns the total number of failures
+ def dump
+ @formatters.each{|f| f.start_dump}
+ dump_failures
+ @formatters.each do |f|
+ f.dump_summary(duration, @example_names.length, @failures.length, @pending_count)
+ f.close
+ end
+ @failures.length
+ end
+
+ private
+
+ def clear!
+ @behaviour_names = []
+ @failures = []
+ @pending_count = 0
+ @example_names = []
+ @start_time = nil
+ @end_time = nil
+ end
+
+ def dump_failures
+ return if @failures.empty?
+ @failures.inject(1) do |index, failure|
+ @formatters.each{|f| f.dump_failure(index, failure)}
+ index + 1
+ end
+ end
+
+ def duration
+ return @end_time - @start_time unless (@end_time.nil? or @start_time.nil?)
+ return "0.0"
+ end
+
+ def example_passed(name)
+ @formatters.each{|f| f.example_passed(name)}
+ end
+
+ def example_failed(name, error, failure_location)
+ @backtrace_tweaker.tweak_backtrace(error, failure_location)
+ example_name = "#{@behaviour_names.last} #{name}"
+ failure = Failure.new(example_name, error)
+ @failures << failure
+ @formatters.each{|f| f.example_failed(name, @failures.length, failure)}
+ end
+
+ def example_pending(behaviour_name, example_name, message="Not Yet Implemented")
+ @pending_count += 1
+ @formatters.each{|f| f.example_pending(behaviour_name, example_name, message)}
+ end
+
+ class Failure
+ attr_reader :exception
+
+ def initialize(example_name, exception)
+ @example_name = example_name
+ @exception = exception
+ end
+
+ def header
+ if expectation_not_met?
+ "'#{@example_name}' FAILED"
+ elsif pending_fixed?
+ "'#{@example_name}' FIXED"
+ else
+ "#{@exception.class.name} in '#{@example_name}'"
+ end
+ end
+
+ def pending_fixed?
+ @exception.is_a?(Spec::DSL::PendingFixedError)
+ end
+
+ def expectation_not_met?
+ @exception.is_a?(Spec::Expectations::ExpectationNotMetError)
+ end
+
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/runner/spec_parser.rb b/spec/lib/spec/runner/spec_parser.rb
new file mode 100644
index 000000000..bc9170065
--- /dev/null
+++ b/spec/lib/spec/runner/spec_parser.rb
@@ -0,0 +1,50 @@
+module Spec
+ module Runner
+ # Parses a spec file and finds the nearest example for a given line number.
+ class SpecParser
+ def spec_name_for(io, line_number)
+ source = io.read
+ behaviour, behaviour_line = behaviour_at_line(source, line_number)
+ example, example_line = example_at_line(source, line_number)
+ if behaviour && example && (behaviour_line < example_line)
+ "#{behaviour} #{example}"
+ elsif behaviour
+ behaviour
+ else
+ nil
+ end
+ end
+
+ protected
+
+ def behaviour_at_line(source, line_number)
+ find_above(source, line_number, /^\s*(context|describe)\s+(.*)\s+do/)
+ end
+
+ def example_at_line(source, line_number)
+ find_above(source, line_number, /^\s*(specify|it)\s+(.*)\s+do/)
+ end
+
+ # Returns the context/describe or specify/it name and the line number
+ def find_above(source, line_number, pattern)
+ lines_above_reversed(source, line_number).each_with_index do |line, n|
+ return [parse_description($2), line_number-n] if line =~ pattern
+ end
+ nil
+ end
+
+ def lines_above_reversed(source, line_number)
+ lines = source.split("\n")
+ lines[0...line_number].reverse
+ end
+
+ def parse_description(str)
+ return str[1..-2] if str =~ /^['"].*['"]$/
+ if matches = /^(.*)\s*,\s*['"](.*)['"]$/.match(str)
+ return ::Spec::DSL::Description.generate_description(matches[1], matches[2])
+ end
+ return str
+ end
+ end
+ end
+end
diff --git a/spec/lib/spec/test_case_adapter.rb b/spec/lib/spec/test_case_adapter.rb
new file mode 100755
index 000000000..992e098fd
--- /dev/null
+++ b/spec/lib/spec/test_case_adapter.rb
@@ -0,0 +1,10 @@
+require 'spec/expectations'
+require 'spec/matchers'
+
+module Test
+ module Unit
+ class TestCase
+ include Spec::Matchers
+ end
+ end
+end
diff --git a/spec/lib/spec/translator.rb b/spec/lib/spec/translator.rb
new file mode 100644
index 000000000..c1e07eda4
--- /dev/null
+++ b/spec/lib/spec/translator.rb
@@ -0,0 +1,114 @@
+require 'fileutils'
+
+module Spec
+ class Translator
+ def translate(from, to)
+ from = File.expand_path(from)
+ to = File.expand_path(to)
+ if File.directory?(from)
+ translate_dir(from, to)
+ elsif(from =~ /\.rb$/)
+ translate_file(from, to)
+ end
+ end
+
+ def translate_dir(from, to)
+ FileUtils.mkdir_p(to) unless File.directory?(to)
+ Dir["#{from}/*"].each do |sub_from|
+ path = sub_from[from.length+1..-1]
+ sub_to = File.join(to, path)
+ translate(sub_from, sub_to)
+ end
+ end
+
+ def translate_file(from, to)
+ translation = ""
+ File.open(from) do |io|
+ io.each_line do |line|
+ translation << translate_line(line)
+ end
+ end
+ File.open(to, "w") do |io|
+ io.write(translation)
+ end
+ end
+
+ def translate_line(line)
+ # Translate deprecated mock constraints
+ line.gsub!(/:any_args/, 'any_args')
+ line.gsub!(/:anything/, 'anything')
+ line.gsub!(/:boolean/, 'boolean')
+ line.gsub!(/:no_args/, 'no_args')
+ line.gsub!(/:numeric/, 'an_instance_of(Numeric)')
+ line.gsub!(/:string/, 'an_instance_of(String)')
+
+ return line if line =~ /(should_not|should)_receive/
+
+ line.gsub!(/(^\s*)context([\s*|\(]['|"|A-Z])/, '\1describe\2')
+ line.gsub!(/(^\s*)specify([\s*|\(]['|"|A-Z])/, '\1it\2')
+ line.gsub!(/(^\s*)context_setup(\s*[do|\{])/, '\1before(:all)\2')
+ line.gsub!(/(^\s*)context_teardown(\s*[do|\{])/, '\1after(:all)\2')
+ line.gsub!(/(^\s*)setup(\s*[do|\{])/, '\1before(:each)\2')
+ line.gsub!(/(^\s*)teardown(\s*[do|\{])/, '\1after(:each)\2')
+
+ if line =~ /(.*\.)(should_not|should)(?:_be)(?!_)(.*)/m
+ pre = $1
+ should = $2
+ post = $3
+ be_or_equal = post =~ /(<|>)/ ? "be" : "equal"
+
+ return "#{pre}#{should} #{be_or_equal}#{post}"
+ end
+
+ if line =~ /(.*\.)(should_not|should)_(?!not)\s*(.*)/m
+ pre = $1
+ should = $2
+ post = $3
+
+ post.gsub!(/^raise/, 'raise_error')
+ post.gsub!(/^throw/, 'throw_symbol')
+
+ unless standard_matcher?(post)
+ post = "be_#{post}"
+ end
+
+ # Add parenthesis
+ post.gsub!(/^(\w+)\s+([\w|\.|\,|\(.*\)|\'|\"|\:|@| ]+)(\})/, '\1(\2)\3') # inside a block
+ post.gsub!(/^(redirect_to)\s+(.*)/, '\1(\2)') # redirect_to, which often has http:
+ post.gsub!(/^(\w+)\s+([\w|\.|\,|\(.*\)|\{.*\}|\'|\"|\:|@| ]+)/, '\1(\2)')
+ post.gsub!(/(\s+\))/, ')')
+ post.gsub!(/\)\}/, ') }')
+ post.gsub!(/^(\w+)\s+(\/.*\/)/, '\1(\2)') #regexps
+ line = "#{pre}#{should} #{post}"
+ end
+
+ line
+ end
+
+ def standard_matcher?(matcher)
+ patterns = [
+ /^be/,
+ /^be_close/,
+ /^eql/,
+ /^equal/,
+ /^has/,
+ /^have/,
+ /^change/,
+ /^include/,
+ /^match/,
+ /^raise_error/,
+ /^respond_to/,
+ /^redirect_to/,
+ /^satisfy/,
+ /^throw_symbol/,
+ # Extra ones that we use in spec_helper
+ /^pass/,
+ /^fail/,
+ /^fail_with/,
+ ]
+ matched = patterns.detect{ |p| matcher =~ p }
+ !matched.nil?
+ end
+
+ end
+end
diff --git a/spec/lib/spec/version.rb b/spec/lib/spec/version.rb
new file mode 100644
index 000000000..5b1db9b37
--- /dev/null
+++ b/spec/lib/spec/version.rb
@@ -0,0 +1,23 @@
+module Spec
+ module VERSION
+ unless defined? MAJOR
+ MAJOR = 1
+ MINOR = 0
+ TINY = 8
+ RELEASE_CANDIDATE = nil
+
+ # RANDOM_TOKEN: 0.510454315029681
+ REV = "$LastChangedRevision: 2338 $".match(/LastChangedRevision: (\d+)/)[1]
+
+ STRING = [MAJOR, MINOR, TINY].join('.')
+ TAG = "REL_#{[MAJOR, MINOR, TINY, RELEASE_CANDIDATE].compact.join('_')}".upcase.gsub(/\.|-/, '_')
+ FULL_VERSION = "#{[MAJOR, MINOR, TINY, RELEASE_CANDIDATE].compact.join('.')} (r#{REV})"
+
+ NAME = "RSpec"
+ URL = "http://rspec.rubyforge.org/"
+
+ DESCRIPTION = "#{NAME}-#{FULL_VERSION} - BDD for Ruby\n#{URL}"
+ end
+ end
+end
+
diff --git a/spec/plugins/mock_frameworks/flexmock.rb b/spec/plugins/mock_frameworks/flexmock.rb
new file mode 100644
index 000000000..6875a5222
--- /dev/null
+++ b/spec/plugins/mock_frameworks/flexmock.rb
@@ -0,0 +1,23 @@
+#!/usr/bin/env ruby
+#
+# Created by Jim Weirich on 2007-04-10.
+# Copyright (c) 2007. All rights reserved.
+
+require 'flexmock/rspec'
+
+module Spec
+ module Plugins
+ module MockFramework
+ include FlexMock::MockContainer
+ def setup_mocks_for_rspec
+ # No setup required
+ end
+ def verify_mocks_for_rspec
+ flexmock_verify
+ end
+ def teardown_mocks_for_rspec
+ flexmock_close
+ end
+ end
+ end
+end
diff --git a/spec/plugins/mock_frameworks/mocha.rb b/spec/plugins/mock_frameworks/mocha.rb
new file mode 100644
index 000000000..69d11636c
--- /dev/null
+++ b/spec/plugins/mock_frameworks/mocha.rb
@@ -0,0 +1,19 @@
+require 'mocha/standalone'
+require 'mocha/object'
+
+module Spec
+ module Plugins
+ module MockFramework
+ include Mocha::Standalone
+ def setup_mocks_for_rspec
+ mocha_setup
+ end
+ def verify_mocks_for_rspec
+ mocha_verify
+ end
+ def teardown_mocks_for_rspec
+ mocha_teardown
+ end
+ end
+ end
+end
diff --git a/spec/plugins/mock_frameworks/rr.rb b/spec/plugins/mock_frameworks/rr.rb
new file mode 100644
index 000000000..c019c18a1
--- /dev/null
+++ b/spec/plugins/mock_frameworks/rr.rb
@@ -0,0 +1,21 @@
+require 'rr'
+
+patterns = ::Spec::Runner::QuietBacktraceTweaker::IGNORE_PATTERNS
+patterns.push(RR::Errors::BACKTRACE_IDENTIFIER)
+
+module Spec
+ module Plugins
+ module MockFramework
+ include RR::Extensions::InstanceMethods
+ def setup_mocks_for_rspec
+ RR::Space.instance.reset
+ end
+ def verify_mocks_for_rspec
+ RR::Space.instance.verify_doubles
+ end
+ def teardown_mocks_for_rspec
+ RR::Space.instance.reset
+ end
+ end
+ end
+end
diff --git a/spec/plugins/mock_frameworks/rspec.rb b/spec/plugins/mock_frameworks/rspec.rb
new file mode 100644
index 000000000..e606c3089
--- /dev/null
+++ b/spec/plugins/mock_frameworks/rspec.rb
@@ -0,0 +1,18 @@
+require File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "lib", "spec", "mocks"))
+
+module Spec
+ module Plugins
+ module MockFramework
+ include Spec::Mocks::SpecMethods
+ def setup_mocks_for_rspec
+ $rspec_mocks ||= Spec::Mocks::Space.new
+ end
+ def verify_mocks_for_rspec
+ $rspec_mocks.verify_all
+ end
+ def teardown_mocks_for_rspec
+ $rspec_mocks.reset_all
+ end
+ end
+ end
+end
diff --git a/spec/spec.opts b/spec/spec.opts
new file mode 100644
index 000000000..2cac5f260
--- /dev/null
+++ b/spec/spec.opts
@@ -0,0 +1,5 @@
+--colour
+--format
+s
+--loadby
+mtime \ No newline at end of file
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
new file mode 100644
index 000000000..aa56fd93e
--- /dev/null
+++ b/spec/spec_helper.rb
@@ -0,0 +1,13 @@
+dir = File.dirname(__FILE__)
+$:.unshift("#{dir}/lib").unshift("#{dir}/../lib")
+
+# Add the old test dir, so that we can still find mocha and spec
+$:.unshift("#{dir}/../test/lib")
+
+require 'mocha'
+require 'spec'
+require 'puppettest'
+
+Spec::Runner.configure do |config|
+ config.mock_with :mocha
+end
diff --git a/spec/unit/node/configuration.rb b/spec/unit/node/configuration.rb
new file mode 100755
index 000000000..4429fe3a3
--- /dev/null
+++ b/spec/unit/node/configuration.rb
@@ -0,0 +1,135 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::Node::Configuration, " when compiling" do
+ it "should accept tags" do
+ config = Puppet::Node::Configuration.new("mynode")
+ config.tag("one")
+ config.tags.should == %w{one}
+ end
+
+ it "should accept multiple tags at once" do
+ config = Puppet::Node::Configuration.new("mynode")
+ config.tag("one", "two")
+ config.tags.should == %w{one two}
+ end
+
+ it "should convert all tags to strings" do
+ config = Puppet::Node::Configuration.new("mynode")
+ config.tag("one", :two)
+ config.tags.should == %w{one two}
+ end
+
+ it "should tag with both the qualified name and the split name" do
+ config = Puppet::Node::Configuration.new("mynode")
+ config.tag("one::two")
+ config.tags.include?("one").should be_true
+ config.tags.include?("one::two").should be_true
+ end
+
+ it "should accept classes" do
+ config = Puppet::Node::Configuration.new("mynode")
+ config.add_class("one")
+ config.classes.should == %w{one}
+ config.add_class("two", "three")
+ config.classes.should == %w{one two three}
+ end
+
+ it "should tag itself with passed class names" do
+ config = Puppet::Node::Configuration.new("mynode")
+ config.add_class("one")
+ config.tags.should == %w{one}
+ end
+end
+
+describe Puppet::Node::Configuration, " when extracting" do
+ it "should return extraction result as the method result" do
+ config = Puppet::Node::Configuration.new("mynode")
+ config.expects(:extraction_format).returns(:whatever)
+ config.expects(:extract_to_whatever).returns(:result)
+ config.extract.should == :result
+ end
+end
+
+describe Puppet::Node::Configuration, " when extracting transobjects" do
+
+ def mkscope
+ @parser = Puppet::Parser::Parser.new :Code => ""
+ @node = Puppet::Node.new("mynode")
+ @compile = Puppet::Parser::Compile.new(@node, @parser)
+
+ # XXX This is ridiculous.
+ @compile.send(:evaluate_main)
+ @scope = @compile.topscope
+ end
+
+ def mkresource(type, name)
+ Puppet::Parser::Resource.new(:type => type, :title => name, :source => @source, :scope => @scope)
+ end
+
+ # This isn't really a spec-style test, but I don't know how better to do it.
+ it "should transform the resource graph into a tree of TransBuckets and TransObjects" do
+ config = Puppet::Node::Configuration.new("mynode")
+
+ @scope = mkscope
+ @source = mock 'source'
+
+ defined = mkresource("class", :main)
+ builtin = mkresource("file", "/yay")
+
+ config.add_edge!(defined, builtin)
+
+ bucket = []
+ bucket.expects(:classes=).with(config.classes)
+ defined.stubs(:builtin?).returns(false)
+ defined.expects(:to_transbucket).returns(bucket)
+ builtin.expects(:to_transobject).returns(:builtin)
+
+ config.extract_to_transportable.should == [:builtin]
+ end
+
+ # Now try it with a more complicated graph -- a three tier graph, each tier
+ it "should transform arbitrarily deep graphs into isomorphic trees" do
+ config = Puppet::Node::Configuration.new("mynode")
+
+ @scope = mkscope
+ @scope.stubs(:tags).returns([])
+ @source = mock 'source'
+
+ # Create our scopes.
+ top = mkresource "class", :main
+ topbucket = []
+ topbucket.expects(:classes=).with([])
+ top.expects(:to_trans).returns(topbucket)
+ topres = mkresource "file", "/top"
+ topres.expects(:to_trans).returns(:topres)
+ config.add_edge! top, topres
+
+ middle = mkresource "class", "middle"
+ middle.expects(:to_trans).returns([])
+ config.add_edge! top, middle
+ midres = mkresource "file", "/mid"
+ midres.expects(:to_trans).returns(:midres)
+ config.add_edge! middle, midres
+
+ bottom = mkresource "class", "bottom"
+ bottom.expects(:to_trans).returns([])
+ config.add_edge! middle, bottom
+ botres = mkresource "file", "/bot"
+ botres.expects(:to_trans).returns(:botres)
+ config.add_edge! bottom, botres
+
+ toparray = config.extract_to_transportable
+
+ # This is annoying; it should look like:
+ # [[[:botres], :midres], :topres]
+ # but we can't guarantee sort order.
+ toparray.include?(:topres).should be_true
+
+ midarray = toparray.find { |t| t.is_a?(Array) }
+ midarray.include?(:midres).should be_true
+ botarray = midarray.find { |t| t.is_a?(Array) }
+ botarray.include?(:botres).should be_true
+ end
+end
diff --git a/spec/unit/other/modules.rb b/spec/unit/other/modules.rb
new file mode 100755
index 000000000..0ab37aa9e
--- /dev/null
+++ b/spec/unit/other/modules.rb
@@ -0,0 +1,156 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::Module, " when building its search path" do
+ include PuppetTest
+
+ it "should ignore unqualified paths in the search path" do
+ Puppet[:modulepath] = "something:/my/something"
+ File.stubs(:directory?).returns(true)
+ Puppet::Module.modulepath.should == %w{/my/something}
+ end
+
+ it "should ignore paths that do not exist" do
+ Puppet[:modulepath] = "/yes:/no"
+ File.expects(:directory?).with("/yes").returns(true)
+ File.expects(:directory?).with("/no").returns(false)
+ Puppet::Module.modulepath.should == %w{/yes}
+ end
+
+ it "should prepend PUPPETLIB in search path when set" do
+ Puppet[:modulepath] = "/my/mod:/other/mod"
+ ENV["PUPPETLIB"] = "/env/mod:/myenv/mod"
+ File.stubs(:directory?).returns(true)
+ Puppet::Module.modulepath.should == %w{/env/mod /myenv/mod /my/mod /other/mod}
+ end
+
+ it "should use the environment-specific search path when a node environment is provided" do
+ Puppet.config.expects(:value).with(:modulepath, "myenv").returns("/mone:/mtwo")
+ File.stubs(:directory?).returns(true)
+ Puppet::Module.modulepath("myenv").should == %w{/mone /mtwo}
+ end
+
+ after do
+ ENV["PUPPETLIB"] = nil
+ end
+end
+
+describe Puppet::Module, " when searching for modules" do
+ it "should find modules in the search path" do
+ path = %w{/dir/path}
+ Puppet::Module.stubs(:modulepath).returns(path)
+ File.stubs(:directory?).returns(true)
+ mod = Puppet::Module.find("mymod")
+ mod.should be_an_instance_of(Puppet::Module)
+ mod.path.should == "/dir/path/mymod"
+ end
+
+ it "should not search for fully qualified modules" do
+ path = %w{/dir/path}
+ Puppet::Module.expects(:modulepath).never
+ File.expects(:directory?).never
+ Puppet::Module.find("/mymod").should be_nil
+ end
+
+ it "should search for modules in the order specified in the search path" do
+ Puppet[:modulepath] = "/one:/two:/three"
+ Puppet::Module.stubs(:modulepath).returns %w{/one /two /three}
+ File.expects(:directory?).with("/one/mod").returns(false)
+ File.expects(:directory?).with("/two/mod").returns(true)
+ File.expects(:directory?).with("/three/mod").never
+ mod = Puppet::Module.find("mod")
+ mod.path.should == "/two/mod"
+ end
+
+ it "should use a node environment if specified" do
+ Puppet::Module.expects(:modulepath).with("myenv").returns([])
+ Puppet::Module.find("mymod", "myenv")
+ end
+end
+
+describe Puppet::Module, " when searching for templates" do
+ it "should return fully-qualified templates directly" do
+ Puppet::Module.expects(:modulepath).never
+ Puppet::Module.find_template("/my/template").should == "/my/template"
+ end
+
+ it "should return the template from the first found module" do
+ Puppet[:modulepath] = "/one:/two"
+ File.stubs(:directory?).returns(true)
+ Puppet::Module.find_template("mymod/mytemplate").should == "/one/mymod/templates/mytemplate"
+ end
+
+ it "should use the main templatedir if no module is found" do
+ Puppet.config.expects(:value).with(:templatedir, nil).returns("/my/templates")
+ Puppet::Module.expects(:find).with("mymod", nil).returns(nil)
+ Puppet::Module.find_template("mymod/mytemplate").should == "/my/templates/mymod/mytemplate"
+ end
+
+ it "should return unqualified templates directly in the template dir" do
+ Puppet.config.expects(:value).with(:templatedir, nil).returns("/my/templates")
+ Puppet::Module.expects(:find).never
+ Puppet::Module.find_template("mytemplate").should == "/my/templates/mytemplate"
+ end
+
+ it "should use the environment templatedir if no module is found and an environment is specified" do
+ Puppet.config.expects(:value).with(:templatedir, "myenv").returns("/myenv/templates")
+ Puppet::Module.expects(:find).with("mymod", "myenv").returns(nil)
+ Puppet::Module.find_template("mymod/mytemplate", "myenv").should == "/myenv/templates/mymod/mytemplate"
+ end
+
+ it "should use the node environment if specified" do
+ Puppet.config.expects(:value).with(:modulepath, "myenv").returns("/my/templates")
+ File.stubs(:directory?).returns(true)
+ Puppet::Module.find_template("mymod/envtemplate", "myenv").should == "/my/templates/mymod/templates/envtemplate"
+ end
+
+ after { Puppet.config.clear }
+end
+
+describe Puppet::Module, " when searching for manifests" do
+ it "should return the manifests from the first found module" do
+ Puppet[:modulepath] = "/one:/two"
+ File.stubs(:directory?).returns(true)
+ Dir.expects(:glob).with("/one/mymod/manifests/init.pp").returns(%w{/one/mymod/manifests/init.pp})
+ Puppet::Module.find_manifests("mymod/init.pp").should == ["/one/mymod/manifests/init.pp"]
+ end
+
+ it "should search the cwd if no module is found" do
+ Puppet[:modulepath] = "/one:/two"
+ File.stubs(:find).returns(nil)
+ cwd = Dir.getwd
+ Dir.expects(:glob).with("#{cwd}/mymod/init.pp").returns(["#{cwd}/mymod/init.pp"])
+ Puppet::Module.find_manifests("mymod/init.pp").should == ["#{cwd}/mymod/init.pp"]
+ end
+
+ it "should use the node environment if specified" do
+ Puppet.config.expects(:value).with(:modulepath, "myenv").returns("/env/modules")
+ File.stubs(:directory?).returns(true)
+ Dir.expects(:glob).with("/env/modules/mymod/manifests/envmanifest.pp").returns(%w{/env/modules/mymod/manifests/envmanifest.pp})
+ Puppet::Module.find_manifests("mymod/envmanifest.pp", :environment => "myenv").should == ["/env/modules/mymod/manifests/envmanifest.pp"]
+ end
+
+ it "should return all manifests matching the glob pattern" do
+ Puppet.config.expects(:value).with(:modulepath, nil).returns("/my/modules")
+ File.stubs(:directory?).returns(true)
+ Dir.expects(:glob).with("/my/modules/mymod/manifests/yay/*.pp").returns(%w{/one /two})
+ Puppet::Module.find_manifests("mymod/yay/*.pp").should == %w{/one /two}
+ end
+
+ it "should default to the 'init.pp' file in the manifests directory" do
+ Puppet.config.expects(:value).with(:modulepath, nil).returns("/my/modules")
+ File.stubs(:directory?).returns(true)
+ Dir.expects(:glob).with("/my/modules/mymod/manifests/init.pp").returns(%w{my manifest})
+ Puppet::Module.find_manifests("mymod").should == %w{my manifest}
+ end
+
+ after { Puppet.config.clear }
+end
+
+describe Puppet::Module, " when returning files" do
+ it "should return the path to the module's 'files' directory" do
+ mod = Puppet::Module.send(:new, "mymod", "/my/mod")
+ mod.files.should == "/my/mod/files"
+ end
+end
diff --git a/spec/unit/other/node.rb b/spec/unit/other/node.rb
new file mode 100755
index 000000000..66d5ba9d7
--- /dev/null
+++ b/spec/unit/other/node.rb
@@ -0,0 +1,101 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::Node, " when initializing" do
+ before do
+ @node = Puppet::Node.new("testnode")
+ end
+
+ it "should set the node name" do
+ @node.name.should == "testnode"
+ end
+
+ it "should default to an empty parameter hash" do
+ @node.parameters.should == {}
+ end
+
+ it "should default to an empty class array" do
+ @node.classes.should == []
+ end
+
+ it "should note its creation time" do
+ @node.time.should be_instance_of(Time)
+ end
+
+ it "should accept parameters passed in during initialization" do
+ params = {"a" => "b"}
+ @node = Puppet::Node.new("testing", :parameters => params)
+ @node.parameters.should == params
+ end
+
+ it "should accept classes passed in during initialization" do
+ classes = %w{one two}
+ @node = Puppet::Node.new("testing", :classes => classes)
+ @node.classes.should == classes
+ end
+
+ it "should always return classes as an array" do
+ @node = Puppet::Node.new("testing", :classes => "myclass")
+ @node.classes.should == ["myclass"]
+ end
+
+ it "should accept the environment during initialization" do
+ @node = Puppet::Node.new("testing", :environment => "myenv")
+ @node.environment.should == "myenv"
+ end
+
+ it "should accept names passed in" do
+ @node = Puppet::Node.new("testing", :names => ["myenv"])
+ @node.names.should == ["myenv"]
+ end
+end
+
+describe Puppet::Node, " when returning the environment" do
+ before do
+ @node = Puppet::Node.new("testnode")
+ end
+
+ it "should return the 'environment' fact if present and there is no explicit environment" do
+ @node.parameters = {"environment" => "myenv"}
+ @node.environment.should == "myenv"
+ end
+
+ it "should return the central environment if there is no environment fact nor explicit environment" do
+ Puppet.config.expects(:[]).with(:environment).returns(:centralenv)
+ @node.environment.should == :centralenv
+ end
+
+ it "should not use an explicit environment that is an empty string" do
+ @node.environment == ""
+ @node.environment.should be_nil
+ end
+
+ it "should not use an environment fact that is an empty string" do
+ @node.parameters = {"environment" => ""}
+ @node.environment.should be_nil
+ end
+
+ it "should not use an explicit environment that is an empty string" do
+ Puppet.config.expects(:[]).with(:environment).returns(nil)
+ @node.environment.should be_nil
+ end
+end
+
+describe Puppet::Node, " when merging facts" do
+ before do
+ @node = Puppet::Node.new("testnode")
+ end
+
+ it "should prefer parameters already set on the node over facts from the node" do
+ @node.parameters = {"one" => "a"}
+ @node.fact_merge("one" => "c")
+ @node.parameters["one"].should == "a"
+ end
+
+ it "should add passed parameters to the parameter list" do
+ @node.parameters = {"one" => "a"}
+ @node.fact_merge("two" => "b")
+ @node.parameters["two"].should == "b"
+ end
+end
diff --git a/spec/unit/parser/compile.rb b/spec/unit/parser/compile.rb
new file mode 100755
index 000000000..f6b3c9b3f
--- /dev/null
+++ b/spec/unit/parser/compile.rb
@@ -0,0 +1,52 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::Parser::Compile, " when compiling" do
+ before do
+ @node = stub 'node', :name => 'mynode'
+ @parser = stub 'parser', :version => "1.0"
+ @compile = Puppet::Parser::Compile.new(@node, @parser)
+ end
+
+ def compile_methods
+ [:set_node_parameters, :evaluate_main, :evaluate_ast_node, :evaluate_node_classes, :evaluate_generators, :fail_on_unevaluated,
+ :finish, :store, :extract]
+ end
+
+ # Stub all of the main compile methods except the ones we're specifically interested in.
+ def compile_stub(*except)
+ (compile_methods - except).each { |m| @compile.stubs(m) }
+ end
+
+ it "should set node parameters as variables in the top scope" do
+ params = {"a" => "b", "c" => "d"}
+ @node.stubs(:parameters).returns(params)
+ compile_stub(:set_node_parameters)
+ @compile.compile
+ @compile.topscope.lookupvar("a").should == "b"
+ @compile.topscope.lookupvar("c").should == "d"
+ end
+
+ it "should evaluate any existing classes named in the node" do
+ classes = %w{one two three four}
+ main = stub 'main'
+ one = stub 'one', :classname => "one"
+ three = stub 'three', :classname => "three"
+ @node.stubs(:name).returns("whatever")
+ @compile.parser.expects(:findclass).with("", "").returns(main)
+ @compile.parser.expects(:findclass).with("", "one").returns(one)
+ @compile.parser.expects(:findclass).with("", "two").returns(nil)
+ @compile.parser.expects(:findclass).with("", "three").returns(three)
+ @compile.parser.expects(:findclass).with("", "four").returns(nil)
+ @node.stubs(:classes).returns(classes)
+ @compile.send :evaluate_main
+ @compile.send :evaluate_node_classes
+
+ # Now make sure we've created the appropriate resources.
+ @compile.resources.find { |r| r.to_s == "Class[one]" }.should be_an_instance_of(Puppet::Parser::Resource)
+ @compile.resources.find { |r| r.to_s == "Class[three]" }.should be_an_instance_of(Puppet::Parser::Resource)
+ @compile.resources.find { |r| r.to_s == "Class[two]" }.should be_nil
+ @compile.resources.find { |r| r.to_s == "Class[four]" }.should be_nil
+ end
+end
diff --git a/spec/unit/parser/interpreter.rb b/spec/unit/parser/interpreter.rb
new file mode 100755
index 000000000..c0f9d54b3
--- /dev/null
+++ b/spec/unit/parser/interpreter.rb
@@ -0,0 +1,194 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::Parser::Interpreter, " when initializing" do
+ it "should default to neither code nor file" do
+ interp = Puppet::Parser::Interpreter.new
+ interp.code.should be_nil
+ interp.file.should be_nil
+ end
+
+ it "should set the code to parse" do
+ interp = Puppet::Parser::Interpreter.new :Code => :code
+ interp.code.should equal(:code)
+ end
+
+ it "should set the file to parse" do
+ interp = Puppet::Parser::Interpreter.new :Manifest => :file
+ interp.file.should equal(:file)
+ end
+
+ it "should set code and ignore manifest when both are present" do
+ interp = Puppet::Parser::Interpreter.new :Code => :code, :Manifest => :file
+ interp.code.should equal(:code)
+ interp.file.should be_nil
+ end
+
+ it "should default to usenodes" do
+ interp = Puppet::Parser::Interpreter.new
+ interp.usenodes?.should be_true
+ end
+
+ it "should allow disabling of usenodes" do
+ interp = Puppet::Parser::Interpreter.new :UseNodes => false
+ interp.usenodes?.should be_false
+ end
+end
+
+describe Puppet::Parser::Interpreter, " when creating parser instances" do
+ before do
+ @interp = Puppet::Parser::Interpreter.new
+ @parser = mock('parser')
+ end
+
+ it "should create a parser with code passed in at initialization time" do
+ @interp.code = :some_code
+ @parser.expects(:string=).with(:some_code)
+ @parser.expects(:parse)
+ Puppet::Parser::Parser.expects(:new).with(:environment => :myenv).returns(@parser)
+ @interp.send(:create_parser, :myenv).object_id.should equal(@parser.object_id)
+ end
+
+ it "should create a parser with a file passed in at initialization time" do
+ @interp.file = :a_file
+ @parser.expects(:file=).with(:a_file)
+ @parser.expects(:parse)
+ Puppet::Parser::Parser.expects(:new).with(:environment => :myenv).returns(@parser)
+ @interp.send(:create_parser, :myenv).should equal(@parser)
+ end
+
+ it "should create a parser with the main manifest when passed neither code nor file" do
+ @parser.expects(:parse)
+ @parser.expects(:file=).with(Puppet[:manifest])
+ Puppet::Parser::Parser.expects(:new).with(:environment => :myenv).returns(@parser)
+ @interp.send(:create_parser, :myenv).should equal(@parser)
+ end
+
+ it "should return nothing when new parsers fail" do
+ Puppet::Parser::Parser.expects(:new).with(:environment => :myenv).raises(ArgumentError)
+ proc { @interp.send(:create_parser, :myenv) }.should raise_error(Puppet::Error)
+ end
+
+ it "should create parsers with environment-appropriate manifests" do
+ # Set our per-environment values. We can't just stub :value, because
+ # it's called by too much of the rest of the code.
+ text = "[env1]\nmanifest = /t/env1.pp\n[env2]\nmanifest = /t/env2.pp"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/whatever")
+ Puppet.config.stubs(:read_file).with(file).returns(text)
+ Puppet.config.parse(file)
+
+ parser1 = mock 'parser1'
+ Puppet::Parser::Parser.expects(:new).with(:environment => :env1).returns(parser1)
+ parser1.expects(:file=).with("/t/env1.pp")
+ parser1.expects(:parse)
+ @interp.send(:create_parser, :env1)
+
+ parser2 = mock 'parser2'
+ Puppet::Parser::Parser.expects(:new).with(:environment => :env2).returns(parser2)
+ parser2.expects(:file=).with("/t/env2.pp")
+ parser2.expects(:parse)
+ @interp.send(:create_parser, :env2)
+ end
+end
+
+describe Puppet::Parser::Interpreter, " when managing parser instances" do
+ before do
+ @interp = Puppet::Parser::Interpreter.new
+ @parser = mock('parser')
+ end
+
+ it "it should an exception when nothing is there and nil is returned" do
+ @interp.expects(:create_parser).with(:myenv).returns(nil)
+ @interp.send(:parser, :myenv).should be_nil
+ end
+
+ it "should create and return a new parser and use the same parser when the parser does not need reparsing" do
+ @interp.expects(:create_parser).with(:myenv).returns(@parser)
+ @interp.send(:parser, :myenv).should equal(@parser)
+
+ @parser.expects(:reparse?).returns(false)
+ @interp.send(:parser, :myenv).should equal(@parser)
+ end
+
+ it "should create a new parser when reparse is true" do
+ oldparser = mock('oldparser')
+ newparser = mock('newparser')
+ oldparser.expects(:reparse?).returns(true)
+ oldparser.expects(:clear)
+
+ @interp.expects(:create_parser).with(:myenv).returns(oldparser)
+ @interp.send(:parser, :myenv).should equal(oldparser)
+ @interp.expects(:create_parser).with(:myenv).returns(newparser)
+ @interp.send(:parser, :myenv).should equal(newparser)
+ end
+
+ it "should keep the old parser if create_parser doesn't return anything." do
+ # Get the first parser in the hash.
+ @interp.expects(:create_parser).with(:myenv).returns(@parser)
+ @interp.send(:parser, :myenv).should equal(@parser)
+
+ # Have it indicate something has changed
+ @parser.expects(:reparse?).returns(true)
+
+ # But fail to create a new parser
+ @interp.expects(:create_parser).with(:myenv).returns(nil)
+
+ # And make sure we still get the old valid parser
+ @interp.send(:parser, :myenv).should equal(@parser)
+ end
+
+ it "should use different parsers for different environments" do
+ # get one for the first env
+ @interp.expects(:create_parser).with(:first_env).returns(@parser)
+ @interp.send(:parser, :first_env).should equal(@parser)
+
+ other_parser = mock('otherparser')
+ @interp.expects(:create_parser).with(:second_env).returns(other_parser)
+ @interp.send(:parser, :second_env).should equal(other_parser)
+ end
+end
+
+describe Puppet::Parser::Interpreter, " when compiling configurations" do
+ before do
+ @interp = Puppet::Parser::Interpreter.new
+ end
+
+ it "should create a configuration with the node, parser, and whether to use ast nodes" do
+ node = mock('node')
+ node.expects(:environment).returns(:myenv)
+ compile = mock 'compile'
+ compile.expects(:compile).returns(:config)
+ parser = mock 'parser'
+ @interp.expects(:parser).with(:myenv).returns(parser)
+ @interp.expects(:usenodes?).returns(true)
+ Puppet::Parser::Compile.expects(:new).with(node, parser, :ast_nodes => true).returns(compile)
+ @interp.compile(node)
+
+ # Now try it when usenodes is true
+ @interp = Puppet::Parser::Interpreter.new :UseNodes => false
+ node.expects(:environment).returns(:myenv)
+ compile.expects(:compile).returns(:config)
+ @interp.expects(:parser).with(:myenv).returns(parser)
+ @interp.expects(:usenodes?).returns(false)
+ Puppet::Parser::Compile.expects(:new).with(node, parser, :ast_nodes => false).returns(compile)
+ @interp.compile(node).should equal(:config)
+ end
+end
+
+describe Puppet::Parser::Interpreter, " when returning configuration version" do
+ before do
+ @interp = Puppet::Parser::Interpreter.new
+ end
+
+ it "should ask the appropriate parser for the configuration version" do
+ node = mock 'node'
+ node.expects(:environment).returns(:myenv)
+ parser = mock 'parser'
+ parser.expects(:version).returns(:myvers)
+ @interp.expects(:parser).with(:myenv).returns(parser)
+ @interp.configuration_version(node).should equal(:myvers)
+ end
+end
diff --git a/spec/unit/parser/resource.rb b/spec/unit/parser/resource.rb
new file mode 100755
index 000000000..354690711
--- /dev/null
+++ b/spec/unit/parser/resource.rb
@@ -0,0 +1,39 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+# LAK: FIXME This is just new tests for resources; I have
+# not moved all tests over yet.
+describe Puppet::Parser::Resource, " when evaluating" do
+ before do
+ @type = Puppet::Parser::Resource
+
+ @parser = Puppet::Parser::Parser.new :Code => ""
+ @source = @parser.newclass ""
+ @definition = @parser.newdefine "mydefine"
+ @class = @parser.newclass "myclass"
+ @nodedef = @parser.newnode("mynode")[0]
+ @node = Puppet::Node.new("yaynode")
+ @compile = Puppet::Parser::Compile.new(@node, @parser)
+ @scope = @compile.topscope
+ end
+
+ it "should evaluate the associated AST definition" do
+ res = @type.new(:type => "mydefine", :title => "whatever", :scope => @scope, :source => @source)
+ @definition.expects(:evaluate).with(:scope => @scope, :resource => res)
+
+ res.evaluate
+ end
+
+ it "should evaluate the associated AST class" do
+ res = @type.new(:type => "class", :title => "myclass", :scope => @scope, :source => @source)
+ @class.expects(:evaluate).with(:scope => @scope, :resource => res)
+ res.evaluate
+ end
+
+ it "should evaluate the associated AST node" do
+ res = @type.new(:type => "node", :title => "mynode", :scope => @scope, :source => @source)
+ @nodedef.expects(:evaluate).with(:scope => @scope, :resource => res)
+ res.evaluate
+ end
+end
diff --git a/spec/unit/parser/resource/reference.rb b/spec/unit/parser/resource/reference.rb
new file mode 100755
index 000000000..45af3d938
--- /dev/null
+++ b/spec/unit/parser/resource/reference.rb
@@ -0,0 +1,66 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../../spec_helper'
+
+describe Puppet::Parser::Resource::Reference do
+ before do
+ @type = Puppet::Parser::Resource::Reference
+ end
+
+ it "should require a type" do
+ proc { @type.new(:title => "yay") }.should raise_error(Puppet::DevError)
+ end
+
+ it "should require a title" do
+ proc { @type.new(:type => "file") }.should raise_error(Puppet::DevError)
+ end
+
+ it "should know when it models a builtin type" do
+ ref = @type.new(:type => "file", :title => "/tmp/yay")
+ ref.builtin?.should be_true
+ ref.builtintype.should equal(Puppet::Type.type(:file))
+ end
+
+ it "should return a relationship-style resource reference when asked" do
+ ref = @type.new(:type => "file", :title => "/tmp/yay")
+ ref.to_ref.should == ["file", "/tmp/yay"]
+ end
+
+ it "should return a resource reference string when asked" do
+ ref = @type.new(:type => "file", :title => "/tmp/yay")
+ ref.to_s.should == "File[/tmp/yay]"
+ end
+end
+
+describe Puppet::Parser::Resource::Reference, " when modeling defined types" do
+ before do
+ @type = Puppet::Parser::Resource::Reference
+
+ @parser = Puppet::Parser::Parser.new :Code => ""
+ @definition = @parser.newdefine "mydefine"
+ @class = @parser.newclass "myclass"
+ @nodedef = @parser.newnode("mynode")[0]
+ @node = Puppet::Node.new("yaynode")
+
+ @compile = Puppet::Parser::Compile.new(@node, @parser)
+ end
+
+ it "should be able to model definitions" do
+ ref = @type.new(:type => "mydefine", :title => "/tmp/yay", :scope => @compile.topscope)
+ ref.builtin?.should be_false
+ ref.definedtype.should equal(@definition)
+ end
+
+ it "should be able to model classes" do
+ ref = @type.new(:type => "class", :title => "myclass", :scope => @compile.topscope)
+ ref.builtin?.should be_false
+ ref.definedtype.should equal(@class)
+ end
+
+ it "should be able to model nodes" do
+ ref = @type.new(:type => "node", :title => "mynode", :scope => @compile.topscope)
+ ref.builtin?.should be_false
+ ref.definedtype.object_id.should == @nodedef.object_id
+ end
+end
+
diff --git a/spec/unit/util/config.rb b/spec/unit/util/config.rb
new file mode 100755
index 000000000..348a54893
--- /dev/null
+++ b/spec/unit/util/config.rb
@@ -0,0 +1,408 @@
+#!/usr/bin/env ruby
+
+require File.dirname(__FILE__) + '/../../spec_helper'
+
+describe Puppet::Util::Config, " when specifying defaults" do
+ before do
+ @config = Puppet::Util::Config.new
+ end
+
+ it "should start with no defined parameters" do
+ @config.params.length.should == 0
+ end
+
+ it "should allow specification of default values associated with a section as an array" do
+ @config.setdefaults(:section, :myvalue => ["defaultval", "my description"])
+ end
+
+ it "should not allow duplicate parameter specifications" do
+ @config.setdefaults(:section, :myvalue => ["a", "b"])
+ lambda { @config.setdefaults(:section, :myvalue => ["c", "d"]) }.should raise_error(ArgumentError)
+ end
+
+ it "should allow specification of default values associated with a section as a hash" do
+ @config.setdefaults(:section, :myvalue => {:default => "defaultval", :desc => "my description"})
+ end
+
+ it "should consider defined parameters to be valid" do
+ @config.setdefaults(:section, :myvalue => ["defaultval", "my description"])
+ @config.valid?(:myvalue).should be_true
+ end
+
+ it "should require a description when defaults are specified with an array" do
+ lambda { @config.setdefaults(:section, :myvalue => ["a value"]) }.should raise_error(ArgumentError)
+ end
+
+ it "should require a description when defaults are specified with a hash" do
+ lambda { @config.setdefaults(:section, :myvalue => {:default => "a value"}) }.should raise_error(ArgumentError)
+ end
+
+ it "should support specifying owner, group, and mode when specifying files" do
+ @config.setdefaults(:section, :myvalue => {:default => "/some/file", :owner => "blah", :mode => "boo", :group => "yay", :desc => "whatever"})
+ end
+
+ it "should support specifying a short name" do
+ @config.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"})
+ end
+
+ it "should fail when short names conflict" do
+ @config.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"})
+ lambda { @config.setdefaults(:section, :myvalue => {:default => "w", :desc => "b", :short => "m"}) }.should raise_error(ArgumentError)
+ end
+end
+
+describe Puppet::Util::Config, " when setting values" do
+ before do
+ @config = Puppet::Util::Config.new
+ @config.setdefaults :main, :myval => ["val", "desc"]
+ @config.setdefaults :main, :bool => [true, "desc"]
+ end
+
+ it "should provide a method for setting values from other objects" do
+ @config[:myval] = "something else"
+ @config[:myval].should == "something else"
+ end
+
+ it "should support a getopt-specific mechanism for setting values" do
+ @config.handlearg("--myval", "newval")
+ @config[:myval].should == "newval"
+ end
+
+ it "should support a getopt-specific mechanism for turning booleans off" do
+ @config.handlearg("--no-bool")
+ @config[:bool].should == false
+ end
+
+ it "should support a getopt-specific mechanism for turning booleans on" do
+ # Turn it off first
+ @config[:bool] = false
+ @config.handlearg("--bool")
+ @config[:bool].should == true
+ end
+
+ it "should clear the cache when setting getopt-specific values" do
+ @config.setdefaults :mysection, :one => ["whah", "yay"], :two => ["$one yay", "bah"]
+ @config[:two].should == "whah yay"
+ @config.handlearg("--one", "else")
+ @config[:two].should == "else yay"
+ end
+
+ it "should not clear other values when setting getopt-specific values" do
+ @config[:myval] = "yay"
+ @config.handlearg("--no-bool")
+ @config[:myval].should == "yay"
+ end
+
+ it "should call passed blocks when values are set" do
+ values = []
+ @config.setdefaults(:section, :hooker => {:default => "yay", :desc => "boo", :hook => lambda { |v| values << v }})
+ values.should == []
+
+ @config[:hooker] = "something"
+ values.should == %w{something}
+ end
+
+ it "should munge values using the element-specific methods" do
+ @config[:bool] = "false"
+ @config[:bool].should == false
+ end
+
+ it "should prefer cli values to values set in Ruby code" do
+ @config.handlearg("--myval", "cliarg")
+ @config[:myval] = "memarg"
+ @config[:myval].should == "cliarg"
+ end
+end
+
+describe Puppet::Util::Config, " when returning values" do
+ before do
+ @config = Puppet::Util::Config.new
+ @config.setdefaults :section, :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"], :four => ["$two $three FOUR", "d"]
+ end
+
+ it "should provide a mechanism for returning set values" do
+ @config[:one] = "other"
+ @config[:one].should == "other"
+ end
+
+ it "should interpolate default values for other parameters into returned parameter values" do
+ @config[:one].should == "ONE"
+ @config[:two].should == "ONE TWO"
+ @config[:three].should == "ONE ONE TWO THREE"
+ end
+
+ it "should interpolate default values that themselves need to be interpolated" do
+ @config[:four].should == "ONE TWO ONE ONE TWO THREE FOUR"
+ end
+
+ it "should interpolate set values for other parameters into returned parameter values" do
+ @config[:one] = "on3"
+ @config[:two] = "$one tw0"
+ @config[:three] = "$one $two thr33"
+ @config[:four] = "$one $two $three f0ur"
+ @config[:one].should == "on3"
+ @config[:two].should == "on3 tw0"
+ @config[:three].should == "on3 on3 tw0 thr33"
+ @config[:four].should == "on3 on3 tw0 on3 on3 tw0 thr33 f0ur"
+ end
+
+ it "should not cache interpolated values such that stale information is returned" do
+ @config[:two].should == "ONE TWO"
+ @config[:one] = "one"
+ @config[:two].should == "one TWO"
+ end
+
+ it "should not cache values such that information from one environment is returned for another environment" do
+ text = "[env1]\none = oneval\n[env2]\none = twoval\n"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/whatever")
+ @config.stubs(:read_file).with(file).returns(text)
+ @config.parse(file)
+
+ @config.value(:one, "env1").should == "oneval"
+ @config.value(:one, "env2").should == "twoval"
+ end
+
+ it "should have a name determined by the 'name' parameter" do
+ @config.setdefaults(:whatever, :name => ["something", "yayness"])
+ @config.name.should == :something
+ @config[:name] = :other
+ @config.name.should == :other
+ end
+end
+
+describe Puppet::Util::Config, " when choosing which value to return" do
+ before do
+ @config = Puppet::Util::Config.new
+ @config.setdefaults :section,
+ :one => ["ONE", "a"],
+ :name => ["myname", "w"]
+ end
+
+ it "should return default values if no values have been set" do
+ @config[:one].should == "ONE"
+ end
+
+ it "should return values set on the cli before values set in the configuration file" do
+ text = "[main]\none = fileval\n"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/whatever")
+ @config.stubs(:parse_file).returns(text)
+ @config.handlearg("--one", "clival")
+ @config.parse(file)
+
+ @config[:one].should == "clival"
+ end
+
+ it "should return values set on the cli before values set in Ruby" do
+ @config[:one] = "rubyval"
+ @config.handlearg("--one", "clival")
+ @config[:one].should == "clival"
+ end
+
+ it "should return values set in the executable-specific section before values set in the main section" do
+ text = "[main]\none = mainval\n[myname]\none = nameval\n"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/whatever")
+ @config.stubs(:read_file).with(file).returns(text)
+ @config.parse(file)
+
+ @config[:one].should == "nameval"
+ end
+
+ it "should not return values outside of its search path" do
+ text = "[other]\none = oval\n"
+ file = "/some/file"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/whatever")
+ @config.stubs(:read_file).with(file).returns(text)
+ @config.parse(file)
+ @config[:one].should == "ONE"
+ end
+
+ it "should return values in a specified environment" do
+ text = "[env]\none = envval\n"
+ file = "/some/file"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/whatever")
+ @config.stubs(:read_file).with(file).returns(text)
+ @config.parse(file)
+ @config.value(:one, "env").should == "envval"
+ end
+
+ it "should return values in a specified environment before values in the main or name sections" do
+ text = "[env]\none = envval\n[main]\none = mainval\n[myname]\none = nameval\n"
+ file = "/some/file"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/whatever")
+ @config.stubs(:read_file).with(file).returns(text)
+ @config.parse(file)
+ @config.value(:one, "env").should == "envval"
+ end
+end
+
+describe Puppet::Util::Config, " when parsing its configuration" do
+ before do
+ @config = Puppet::Util::Config.new
+ @config.setdefaults :section, :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"]
+ end
+
+ it "should return values set in the configuration file" do
+ text = "[main]
+ one = fileval
+ "
+ file = "/some/file"
+ @config.expects(:read_file).with(file).returns(text)
+ @config.parse(file)
+ @config[:one].should == "fileval"
+ end
+
+ #484 - this should probably be in the regression area
+ it "should not throw an exception on unknown parameters" do
+ text = "[main]\nnosuchparam = mval\n"
+ file = "/some/file"
+ @config.expects(:read_file).with(file).returns(text)
+ lambda { @config.parse(file) }.should_not raise_error
+ end
+
+ it "should support an old parse method when per-executable configuration files still exist" do
+ # I'm not going to bother testing this method.
+ @config.should respond_to(:old_parse)
+ end
+
+ it "should convert booleans in the configuration file into Ruby booleans" do
+ text = "[main]
+ one = true
+ two = false
+ "
+ file = "/some/file"
+ @config.expects(:read_file).with(file).returns(text)
+ @config.parse(file)
+ @config[:one].should == true
+ @config[:two].should == false
+ end
+
+ it "should convert integers in the configuration file into Ruby Integers" do
+ text = "[main]
+ one = 65
+ "
+ file = "/some/file"
+ @config.expects(:read_file).with(file).returns(text)
+ @config.parse(file)
+ @config[:one].should == 65
+ end
+
+ it "should support specifying file all metadata (owner, group, mode) in the configuration file" do
+ @config.setdefaults :section, :myfile => ["/my/file", "a"]
+
+ text = "[main]
+ myfile = /other/file {owner = luke, group = luke, mode = 644}
+ "
+ file = "/some/file"
+ @config.expects(:read_file).with(file).returns(text)
+ @config.parse(file)
+ @config[:myfile].should == "/other/file"
+ @config.metadata(:myfile).should == {:owner => "luke", :group => "luke", :mode => "644"}
+ end
+
+ it "should support specifying file a single piece of metadata (owner, group, or mode) in the configuration file" do
+ @config.setdefaults :section, :myfile => ["/my/file", "a"]
+
+ text = "[main]
+ myfile = /other/file {owner = luke}
+ "
+ file = "/some/file"
+ @config.expects(:read_file).with(file).returns(text)
+ @config.parse(file)
+ @config[:myfile].should == "/other/file"
+ @config.metadata(:myfile).should == {:owner => "luke"}
+ end
+end
+
+describe Puppet::Util::Config, " when reparsing its configuration" do
+ before do
+ @config = Puppet::Util::Config.new
+ @config.setdefaults :section, :one => ["ONE", "a"], :two => ["$one TWO", "b"], :three => ["$one $two THREE", "c"]
+ end
+
+ it "should replace in-memory values with on-file values" do
+ # Init the value
+ text = "[main]\none = disk-init\n"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/test/file")
+ @config[:one] = "init"
+ @config.file = file
+
+ # Now replace the value
+ text = "[main]\none = disk-replace\n"
+
+ # This is kinda ridiculous - the reason it parses twice is that
+ # it goes to parse again when we ask for the value, because the
+ # mock always says it should get reparsed.
+ @config.expects(:read_file).with(file).returns(text).times(2)
+ @config.reparse
+ @config[:one].should == "disk-replace"
+ end
+
+ it "should retain parameters set by cli when configuration files are reparsed" do
+ @config.handlearg("--one", "clival")
+
+ text = "[main]\none = on-disk\n"
+ file = mock 'file'
+ file.stubs(:file).returns("/test/file")
+ @config.stubs(:read_file).with(file).returns(text)
+ @config.parse(file)
+
+ @config[:one].should == "clival"
+ end
+
+ it "should remove in-memory values that are no longer set in the file" do
+ # Init the value
+ text = "[main]\none = disk-init\n"
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ file.stubs(:file).returns("/test/file")
+ @config.expects(:read_file).with(file).returns(text)
+ @config.parse(file)
+ @config[:one].should == "disk-init"
+
+ # Now replace the value
+ text = "[main]\ntwo = disk-replace\n"
+ @config.expects(:read_file).with(file).returns(text)
+ @config.parse(file)
+ #@config.reparse
+
+ # The originally-overridden value should be replaced with the default
+ @config[:one].should == "ONE"
+
+ # and we should now have the new value in memory
+ @config[:two].should == "disk-replace"
+ end
+end
+
+#describe Puppet::Util::Config, " when being used to manage the host machine" do
+# it "should provide a method that writes files with the correct modes"
+#
+# it "should provide a method that creates directories with the correct modes"
+#
+# it "should provide a method to declare what directories should exist"
+#
+# it "should provide a method to trigger enforcing of file modes on existing files and directories"
+#
+# it "should provide a method to convert the file mode enforcement into a Puppet manifest"
+#
+# it "should provide an option to create needed users and groups"
+#
+# it "should provide a method to print out the current configuration"
+#
+# it "should be able to provide all of its parameters in a format compatible with GetOpt::Long"
+#
+# it "should not attempt to manage files within /dev"
+#end
diff --git a/test/certmgr/certmgr.rb b/test/certmgr/certmgr.rb
index ff0a3b61b..fb1611d7f 100755
--- a/test/certmgr/certmgr.rb
+++ b/test/certmgr/certmgr.rb
@@ -239,14 +239,20 @@ class TestCertMgr < Test::Unit::TestCase
ca.revoke(h1.serial)
+ oldcert = File.read(Puppet.config[:cacert])
+ oldserial = File.read(Puppet.config[:serial])
+
# Recreate the CA from disk
ca = mkCA()
+ newcert = File.read(Puppet.config[:cacert])
+ newserial = File.read(Puppet.config[:serial])
+ assert_equal(oldcert, newcert, "The certs are not equal after making a new CA.")
+ assert_equal(oldserial, newserial, "The serials are not equal after making a new CA.")
store = mkStore(ca)
- assert( store.verify(ca.cert))
- assert(!store.verify(h1, [ca.cert]))
- assert( store.verify(h2, [ca.cert]))
+ assert( store.verify(ca.cert), "Could not verify CA certs after reloading certs.")
+ assert(!store.verify(h1, [ca.cert]), "Incorrectly verified revoked cert.")
+ assert( store.verify(h2, [ca.cert]), "Could not verify certs with reloaded CA.")
- Puppet.err :yay
ca.revoke(h2.serial)
assert_equal(1, ca.crl.extensions.size)
diff --git a/test/language/ast.rb b/test/language/ast.rb
index 9e00c610d..dbc1d04ed 100755
--- a/test/language/ast.rb
+++ b/test/language/ast.rb
@@ -49,29 +49,26 @@ class TestAST < Test::Unit::TestCase
# Make sure our override object behaves "correctly"
def test_override
- interp, scope, source = mkclassframing
+ scope = mkscope
ref = nil
assert_nothing_raised do
- ref = resourceoverride("resource", "yaytest", "one" => "yay", "two" => "boo")
+ ref = resourceoverride("file", "/yayness", "owner" => "blah", "group" => "boo")
end
+ Puppet::Parser::Resource.expects(:new).with { |o| o.is_a?(Hash) }.returns(:override)
+ scope.compile.expects(:store_override).with(:override)
ret = nil
assert_nothing_raised do
ret = ref.evaluate :scope => scope
end
- assert_instance_of(Puppet::Parser::Resource, ret)
-
- assert(ret.override?, "Resource was not an override resource")
-
- assert(scope.overridetable[ret.ref].include?(ret),
- "Was not stored in the override table")
+ assert_equal(:override, ret, "Did not return override")
end
# make sure our resourcedefaults ast object works correctly.
def test_resourcedefaults
- interp, scope, source = mkclassframing
+ scope = mkscope
# Now make some defaults for files
args = {:source => "/yay/ness", :group => "yayness"}
@@ -97,16 +94,16 @@ class TestAST < Test::Unit::TestCase
end
def test_node
- interp = mkinterp
- scope = mkscope(:interp => interp)
+ scope = mkscope
+ parser = scope.compile.parser
# Define a base node
- basenode = interp.newnode "basenode", :code => AST::ASTArray.new(:children => [
+ basenode = parser.newnode "basenode", :code => AST::ASTArray.new(:children => [
resourcedef("file", "/tmp/base", "owner" => "root")
])
# Now define a subnode
- nodes = interp.newnode ["mynode", "othernode"],
+ nodes = parser.newnode ["mynode", "othernode"],
:code => AST::ASTArray.new(:children => [
resourcedef("file", "/tmp/mynode", "owner" => "root"),
resourcedef("file", "/tmp/basenode", "owner" => "daemon")
@@ -116,13 +113,13 @@ class TestAST < Test::Unit::TestCase
# Make sure we can find them all.
%w{mynode othernode}.each do |node|
- assert(interp.nodesearch_code(node), "Could not find %s" % node)
+ assert(parser.nodes[node], "Could not find %s" % node)
end
- mynode = interp.nodesearch_code("mynode")
+ mynode = parser.nodes["mynode"]
# Now try evaluating the node
assert_nothing_raised do
- mynode.evaluate :scope => scope
+ mynode.evaluate :scope => scope, :resource => scope.resource
end
# Make sure that we can find each of the files
@@ -135,11 +132,11 @@ class TestAST < Test::Unit::TestCase
assert_equal("daemon", basefile[:owner])
# Now make sure we can evaluate nodes with parents
- child = interp.newnode(%w{child}, :parent => "basenode").shift
+ child = parser.newnode(%w{child}, :parent => "basenode").shift
- newscope = mkscope :interp => interp
+ newscope = mkscope :parser => parser
assert_nothing_raised do
- child.evaluate :scope => newscope
+ child.evaluate :scope => newscope, :resource => scope.resource
end
assert(newscope.findresource("File[/tmp/base]"),
@@ -147,8 +144,7 @@ class TestAST < Test::Unit::TestCase
end
def test_collection
- interp = mkinterp
- scope = mkscope(:interp => interp)
+ scope = mkscope
coll = nil
assert_nothing_raised do
@@ -165,20 +161,21 @@ class TestAST < Test::Unit::TestCase
assert_instance_of(Puppet::Parser::Collector, ret)
# Now make sure we get it back from the scope
- assert_equal([ret], scope.collections)
+ colls = scope.compile.instance_variable_get("@collections")
+ assert_equal([ret], colls, "Did not store collector in config's collection list")
end
def test_virtual_collexp
- @interp, @scope, @source = mkclassframing
+ scope = mkscope
# make a resource
resource = mkresource(:type => "file", :title => "/tmp/testing",
- :params => {:owner => "root", :group => "bin", :mode => "644"})
+ :scope => scope, :params => {:owner => "root", :group => "bin", :mode => "644"})
run_collection_queries(:virtual) do |string, result, query|
code = nil
assert_nothing_raised do
- str, code = query.evaluate :scope => @scope
+ str, code = query.evaluate :scope => scope
end
assert_instance_of(Proc, code)
@@ -189,5 +186,3 @@ class TestAST < Test::Unit::TestCase
end
end
end
-
-# $Id$
diff --git a/test/language/ast/component.rb b/test/language/ast/definition.rb
index 40543e9ab..d4c987362 100755
--- a/test/language/ast/component.rb
+++ b/test/language/ast/definition.rb
@@ -10,17 +10,17 @@ require 'mocha'
require 'puppettest/parsertesting'
require 'puppettest/resourcetesting'
-class TestASTComponent < Test::Unit::TestCase
+class TestASTDefinition < Test::Unit::TestCase
include PuppetTest
include PuppetTest::ParserTesting
include PuppetTest::ResourceTesting
AST = Puppet::Parser::AST
- def test_component
- interp, scope, source = mkclassframing
+ def test_initialize
+ parser = mkparser
# Create a new definition
- klass = interp.newdefine "yayness",
+ klass = parser.newdefine "yayness",
:arguments => [["owner", stringobj("nobody")], %w{mode}],
:code => AST::ASTArray.new(
:children => [resourcedef("file", "/tmp/$name",
@@ -35,27 +35,38 @@ class TestASTComponent < Test::Unit::TestCase
[:random, "random"].each do |var|
assert(! klass.validattr?(var), "%s was considered valid" % var.inspect)
end
- # Now call it a couple of times
- # First try it without a required param
- assert_raise(Puppet::ParseError) do
- klass.evaluate(:scope => scope,
- :name => "bad",
- :arguments => {"owner" => "nobody"}
- )
- end
- # And make sure it didn't create the file
- assert_nil(scope.findresource("File[/tmp/bad]"),
- "Made file with invalid params")
+ end
- assert_nothing_raised do
- klass.evaluate(:scope => scope,
- :title => "first",
- :arguments => {"mode" => "755"}
+ def test_evaluate
+ parser = mkparser
+ config = mkcompile
+ config.send(:evaluate_main)
+ scope = config.topscope
+ klass = parser.newdefine "yayness",
+ :arguments => [["owner", stringobj("nobody")], %w{mode}],
+ :code => AST::ASTArray.new(
+ :children => [resourcedef("file", "/tmp/$name",
+ "owner" => varref("owner"), "mode" => varref("mode"))]
)
+
+ resource = Puppet::Parser::Resource.new(
+ :title => "first",
+ :type => "yayness",
+ :exported => false,
+ :virtual => false,
+ :scope => scope,
+ :source => scope.source
+ )
+ resource.send(:set_parameter, "name", "first")
+ resource.send(:set_parameter, "mode", "755")
+
+ resource.stubs(:title)
+ assert_nothing_raised do
+ klass.evaluate(:scope => scope, :resource => resource)
end
- firstobj = scope.findresource("File[/tmp/first]")
+ firstobj = config.findresource("File[/tmp/first]")
assert(firstobj, "Did not create /tmp/first obj")
assert_equal("file", firstobj.type)
@@ -65,21 +76,27 @@ class TestASTComponent < Test::Unit::TestCase
# Make sure we can't evaluate it with the same args
assert_raise(Puppet::ParseError) do
- klass.evaluate(:scope => scope,
- :title => "first",
- :arguments => {"mode" => "755"}
- )
+ klass.evaluate(:scope => scope, :resource => resource)
end
# Now create another with different args
+ resource2 = Puppet::Parser::Resource.new(
+ :title => "second",
+ :type => "yayness",
+ :exported => false,
+ :virtual => false,
+ :scope => scope,
+ :source => scope.source
+ )
+ resource2.send(:set_parameter, "name", "second")
+ resource2.send(:set_parameter, "mode", "755")
+ resource2.send(:set_parameter, "owner", "daemon")
+
assert_nothing_raised do
- klass.evaluate(:scope => scope,
- :title => "second",
- :arguments => {"mode" => "755", "owner" => "daemon"}
- )
+ klass.evaluate(:scope => scope, :resource => resource2)
end
- secondobj = scope.findresource("File[/tmp/second]")
+ secondobj = config.findresource("File[/tmp/second]")
assert(secondobj, "Did not create /tmp/second obj")
assert_equal("file", secondobj.type)
@@ -90,32 +107,40 @@ class TestASTComponent < Test::Unit::TestCase
# #539 - definitions should support both names and titles
def test_names_and_titles
- interp, scope, source = mkclassframing
+ parser = mkparser
+ scope = mkscope :parser => parser
[
- {:name => "one", :title => "two"},
- {:title => "mytitle"},
+ {:name => "one", :title => "two"},
+ {:title => "mytitle"}
].each_with_index do |hash, i|
+ # Create a definition that uses both name and title. Put this
+ # inside the loop so the subscope expectations work.
+ klass = parser.newdefine "yayness%s" % i
+
+ resource = Puppet::Parser::Resource.new(
+ :title => hash[:title],
+ :type => "yayness%s" % i,
+ :exported => false,
+ :virtual => false,
+ :scope => scope,
+ :source => scope.source
+ )
- # Create a definition that uses both name and title
- klass = interp.newdefine "yayness%s" % i
-
- subscope = klass.subscope(scope, "yayness%s" % i)
+ subscope = klass.subscope(scope, resource)
klass.expects(:subscope).returns(subscope)
- args = {:title => hash[:title]}
if hash[:name]
- args[:arguments] = {:name => hash[:name]}
+ resource.stubs(:to_hash).returns({:name => hash[:name]})
end
- args[:scope] = scope
+
assert_nothing_raised("Could not evaluate definition with %s" % hash.inspect) do
- klass.evaluate(args)
+ klass.evaluate(:scope => scope, :resource => resource)
end
name = hash[:name] || hash[:title]
title = hash[:title]
- args[:name] ||= name
assert_equal(name, subscope.lookupvar("name"),
"Name did not get set correctly")
@@ -123,9 +148,9 @@ class TestASTComponent < Test::Unit::TestCase
"title did not get set correctly")
[:name, :title].each do |param|
- val = args[param]
+ val = resource.send(param)
assert(subscope.tags.include?(val),
- "Scope was not tagged with %s" % val)
+ "Scope was not tagged with %s '%s'" % [param, val])
end
end
end
@@ -133,10 +158,9 @@ class TestASTComponent < Test::Unit::TestCase
# Testing the root cause of #615. We should be using the fqname for the type, instead
# of just the short name.
def test_fully_qualified_types
- interp = mkinterp
- klass = interp.newclass("one::two")
+ parser = mkparser
+ klass = parser.newclass("one::two")
assert_equal("one::two", klass.classname, "Class did not get fully qualified class name")
end
end
-# $Id$
diff --git a/test/language/ast/hostclass.rb b/test/language/ast/hostclass.rb
index 051bee36c..f747779b3 100755
--- a/test/language/ast/hostclass.rb
+++ b/test/language/ast/hostclass.rb
@@ -17,25 +17,27 @@ class TestASTHostClass < Test::Unit::TestCase
AST = Puppet::Parser::AST
def test_hostclass
- interp, scope, source = mkclassframing
+ scope = mkscope
+ parser = scope.compile.parser
# Create the class we're testing, first with no parent
- klass = interp.newclass "first",
+ klass = parser.newclass "first",
:code => AST::ASTArray.new(
:children => [resourcedef("file", "/tmp",
"owner" => "nobody", "mode" => "755")]
)
+ resource = Puppet::Parser::Resource.new(:type => "class", :title => "first", :scope => scope)
assert_nothing_raised do
- klass.evaluate(:scope => scope)
+ klass.evaluate(:scope => scope, :resource => resource)
end
# Then try it again
assert_nothing_raised do
- klass.evaluate(:scope => scope)
+ klass.evaluate(:scope => scope, :resource => resource)
end
- assert(scope.class_scope(klass), "Class was not considered evaluated")
+ assert(scope.compile.class_scope(klass), "Class was not considered evaluated")
tmp = scope.findresource("File[/tmp]")
assert(tmp, "Could not find file /tmp")
@@ -43,13 +45,13 @@ class TestASTHostClass < Test::Unit::TestCase
assert_equal("755", tmp[:mode])
# Now create a couple more classes.
- newbase = interp.newclass "newbase",
+ newbase = parser.newclass "newbase",
:code => AST::ASTArray.new(
:children => [resourcedef("file", "/tmp/other",
"owner" => "nobody", "mode" => "644")]
)
- newsub = interp.newclass "newsub",
+ newsub = parser.newclass "newsub",
:parent => "newbase",
:code => AST::ASTArray.new(
:children => [resourcedef("file", "/tmp/yay",
@@ -60,7 +62,7 @@ class TestASTHostClass < Test::Unit::TestCase
)
# Override a different variable in the top scope.
- moresub = interp.newclass "moresub",
+ moresub = parser.newclass "moresub",
:parent => "newbase",
:code => AST::ASTArray.new(
:children => [resourceoverride("file", "/tmp/other",
@@ -68,15 +70,15 @@ class TestASTHostClass < Test::Unit::TestCase
)
assert_nothing_raised do
- newsub.evaluate(:scope => scope)
+ newsub.evaluate(:scope => scope, :resource => resource)
end
assert_nothing_raised do
- moresub.evaluate(:scope => scope)
+ moresub.evaluate(:scope => scope, :resource => resource)
end
- assert(scope.class_scope(newbase), "Did not eval newbase")
- assert(scope.class_scope(newsub), "Did not eval newsub")
+ assert(scope.compile.class_scope(newbase), "Did not eval newbase")
+ assert(scope.compile.class_scope(newsub), "Did not eval newsub")
yay = scope.findresource("File[/tmp/yay]")
assert(yay, "Did not find file /tmp/yay")
@@ -92,19 +94,20 @@ class TestASTHostClass < Test::Unit::TestCase
# Make sure that classes set their namespaces to themselves. This
# way they start looking for definitions in their own namespace.
def test_hostclass_namespace
- interp, scope, source = mkclassframing
+ scope = mkscope
+ parser = scope.compile.parser
# Create a new class
klass = nil
assert_nothing_raised do
- klass = interp.newclass "funtest"
+ klass = parser.newclass "funtest"
end
# Now define a definition in that namespace
define = nil
assert_nothing_raised do
- define = interp.newdefine "funtest::mydefine"
+ define = parser.newdefine "funtest::mydefine"
end
assert_equal("funtest", klass.namespace,
@@ -113,7 +116,7 @@ class TestASTHostClass < Test::Unit::TestCase
assert_equal("funtest", define.namespace,
"component namespace was not set in the definition")
- newscope = klass.subscope(scope)
+ newscope = klass.subscope(scope, mock("resource"))
assert_equal(["funtest"], newscope.namespaces,
"Scope did not inherit namespace")
@@ -127,24 +130,27 @@ class TestASTHostClass < Test::Unit::TestCase
# At the same time, make sure definitions in the parent class can be
# found within the subclass (#517).
def test_parent_scope_from_parentclass
- interp = mkinterp
+ scope = mkscope
+ parser = scope.compile.parser
- interp.newclass("base")
- fun = interp.newdefine("base::fun")
- interp.newclass("middle", :parent => "base")
- interp.newclass("sub", :parent => "middle")
- scope = mkscope :interp => interp
+ source = parser.newclass ""
+ parser.newclass("base")
+ fun = parser.newdefine("base::fun")
+ parser.newclass("middle", :parent => "base")
+ parser.newclass("sub", :parent => "middle")
+ scope = mkscope :parser => parser
ret = nil
assert_nothing_raised do
- ret = scope.evalclasses("sub")
+ ret = scope.compile.evaluate_classes(["sub"], scope)
end
+ scope.compile.send(:evaluate_generators)
- subscope = scope.class_scope(scope.findclass("sub"))
+ subscope = scope.compile.class_scope(scope.findclass("sub"))
assert(subscope, "could not find sub scope")
- mscope = scope.class_scope(scope.findclass("middle"))
+ mscope = scope.compile.class_scope(scope.findclass("middle"))
assert(mscope, "could not find middle scope")
- pscope = scope.class_scope(scope.findclass("base"))
+ pscope = scope.compile.class_scope(scope.findclass("base"))
assert(pscope, "could not find parent scope")
assert(pscope == mscope.parent, "parent scope of middle was not set correctly")
@@ -158,6 +164,21 @@ class TestASTHostClass < Test::Unit::TestCase
assert(result, "could not find parent-defined definition from sub")
assert(fun == result, "found incorrect parent-defined definition from sub")
end
-end
-# $Id$
+ # #795 - make sure the subclass's tags get set before we
+ # evaluate the parent class, so we can be sure that the parent
+ # class can switch based on the sub classes.
+ def test_tags_set_before_parent_is_evaluated
+ scope = mkscope
+ parser = scope.compile.parser
+ base = parser.newclass "base"
+ sub = parser.newclass "sub", :parent => "base"
+
+ base.expects(:safeevaluate).with do |args|
+ assert(scope.compile.configuration.tags.include?("sub"), "Did not tag with sub class name before evaluating base class")
+ base.evaluate(args)
+ true
+ end
+ sub.evaluate :scope => scope, :resource => scope.resource
+ end
+end
diff --git a/test/language/ast/resource.rb b/test/language/ast/resource.rb
new file mode 100755
index 000000000..9ef5181af
--- /dev/null
+++ b/test/language/ast/resource.rb
@@ -0,0 +1,59 @@
+#!/usr/bin/env ruby
+#
+# Created by Luke A. Kanies on 2007-07-8.
+# Copyright (c) 2007. All rights reserved.
+
+$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
+
+require 'puppettest'
+require 'puppettest/parsertesting'
+
+class TestASTResource< Test::Unit::TestCase
+ include PuppetTest
+ include PuppetTest::ParserTesting
+ AST = Puppet::Parser::AST
+
+ def setup
+ super
+ @scope = mkscope
+ @parser = @scope.compile.parser
+ @scope.compile.send(:evaluate_main)
+ end
+
+ def newdef(type, title, params = nil)
+ params ||= AST::ASTArray.new(:children => [])
+ AST::Resource.new(:type => type, :title => AST::String.new(:value => title), :params => params)
+ end
+
+ # Related to #806, make sure resources always look up the full path to the resource.
+ def test_scoped_types
+ @parser.newdefine "one"
+ @parser.newdefine "one::two"
+ @parser.newdefine "three"
+ twoscope = @scope.newscope(:namespace => "one")
+ twoscope.resource = @scope.resource
+ assert(twoscope.finddefine("two"), "Could not find 'two' definition")
+ title = "title"
+
+ # First try a qualified type
+ assert_equal("one::two", newdef("two", title).evaluate(:scope => twoscope)[0].type,
+ "Defined type was not made fully qualified")
+
+ # Then try a type that does not need to be qualified
+ assert_equal("one", newdef("one", title).evaluate(:scope => twoscope)[0].type,
+ "Unqualified defined type was not handled correctly")
+
+ # Then an unqualified type from within the one namespace
+ assert_equal("three", newdef("three", title).evaluate(:scope => twoscope)[0].type,
+ "Defined type was not made fully qualified")
+
+ # Then a builtin type
+ assert_equal("file", newdef("file", title).evaluate(:scope => twoscope)[0].type,
+ "Builtin type was not handled correctly")
+
+ # Now try a type that does not exist, which should throw an error.
+ assert_raise(Puppet::ParseError, "Did not fail on a missing type in a resource reference") do
+ newdef("nosuchtype", title).evaluate(:scope => twoscope)
+ end
+ end
+end
diff --git a/test/language/ast/resourceref.rb b/test/language/ast/resource_reference.rb
index 7b7889dc1..e8883afb9 100755
--- a/test/language/ast/resourceref.rb
+++ b/test/language/ast/resource_reference.rb
@@ -8,24 +8,24 @@ $:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
require 'puppettest'
require 'puppettest/parsertesting'
-class TestASTResourceRef < Test::Unit::TestCase
+class TestASTResourceReference < Test::Unit::TestCase
include PuppetTest
include PuppetTest::ParserTesting
AST = Puppet::Parser::AST
def newref(type, title)
- AST::ResourceRef.new(:type => type, :title => AST::String.new(:value => title))
+ AST::ResourceReference.new(:type => type, :title => AST::String.new(:value => title))
end
def setup
super
- @interp = mkinterp
- @scope = mkscope :interp => @interp
+ @scope = mkscope
+ @parser = @scope.compile.parser
end
def test_evaluate
- @interp.newdefine "one::two"
- @interp.newdefine "one-two"
+ @parser.newdefine "one::two"
+ @parser.newdefine "one-two"
[%w{file /tmp/yay}, %w{one::two three}, %w{one-two three}].each do |type, title|
ref = newref(type, title)
@@ -41,10 +41,10 @@ class TestASTResourceRef < Test::Unit::TestCase
# Related to #706, make sure resource references correctly translate to qualified types.
def test_scoped_references
- @interp.newdefine "one"
- @interp.newdefine "one::two"
- @interp.newdefine "three"
- twoscope = @scope.newscope(:type => "one", :namespace => "one")
+ @parser.newdefine "one"
+ @parser.newdefine "one::two"
+ @parser.newdefine "three"
+ twoscope = @scope.newscope(:namespace => "one")
assert(twoscope.finddefine("two"), "Could not find 'two' definition")
title = "title"
@@ -70,8 +70,8 @@ class TestASTResourceRef < Test::Unit::TestCase
end
# Now run the same tests, but with the classes
- @interp.newclass "four"
- @interp.newclass "one::five"
+ @parser.newclass "four"
+ @parser.newclass "one::five"
# First try an unqualified type
assert_equal("four", newref("class", "four").evaluate(:scope => twoscope).title,
@@ -91,5 +91,3 @@ class TestASTResourceRef < Test::Unit::TestCase
end
end
end
-
-# $Id$
diff --git a/test/language/collector.rb b/test/language/collector.rb
index bdcaf4aec..55c93c2d5 100755
--- a/test/language/collector.rb
+++ b/test/language/collector.rb
@@ -16,7 +16,8 @@ class TestCollector < Test::Unit::TestCase
def setup
super
Puppet[:trace] = false
- @interp, @scope, @source = mkclassframing
+ @scope = mkscope
+ @compile = @scope.compile
end
# Test just collecting a specific resource. This is used by the 'realize'
@@ -32,7 +33,7 @@ class TestCollector < Test::Unit::TestCase
assert_nothing_raised do
coll.resources = ["File[/tmp/virtual1]", "File[/tmp/virtual3]"]
end
- @scope.newcollection(coll)
+ @compile.add_collection(coll)
# Evaluate the collector and make sure it doesn't fail with no resources
# found yet
@@ -45,8 +46,8 @@ class TestCollector < Test::Unit::TestCase
:virtual => true, :params => {:owner => "root"})
two = mkresource(:type => "file", :title => "/tmp/virtual2",
:virtual => true, :params => {:owner => "root"})
- @scope.setresource one
- @scope.setresource two
+ @scope.compile.store_resource @scope, one
+ @scope.compile.store_resource @scope, two
# Now run the collector again and make sure it finds our resource
assert_nothing_raised do
@@ -62,12 +63,12 @@ class TestCollector < Test::Unit::TestCase
"Resource got realized")
# Make sure that the collection is still there
- assert(@scope.collections.include?(coll), "collection was deleted too soon")
+ assert(@compile.collections.include?(coll), "collection was deleted too soon")
# Now add our third resource
three = mkresource(:type => "file", :title => "/tmp/virtual3",
:virtual => true, :params => {:owner => "root"})
- @scope.setresource three
+ @scope.compile.store_resource @scope, three
# Run the collection
assert_nothing_raised do
@@ -76,19 +77,19 @@ class TestCollector < Test::Unit::TestCase
assert(! three.virtual?, "three is still virtual")
# And make sure that the collection got deleted from the scope's list
- assert(@scope.collections.empty?, "collection was not deleted")
+ assert(@compile.collections.empty?, "collection was not deleted")
end
def test_virtual
# Make a virtual resource
virtual = mkresource(:type => "file", :title => "/tmp/virtual",
:virtual => true, :params => {:owner => "root"})
- @scope.setresource virtual
+ @scope.compile.store_resource @scope, virtual
# And a non-virtual
real = mkresource(:type => "file", :title => "/tmp/real",
:params => {:owner => "root"})
- @scope.setresource real
+ @scope.compile.store_resource @scope, real
# Now make a collector
coll = nil
@@ -102,10 +103,10 @@ class TestCollector < Test::Unit::TestCase
end
# Set it in our scope
- @scope.newcollection(coll)
+ @compile.add_collection(coll)
# Make sure it's in the collections
- assert(@scope.collections.include?(coll), "collection was not added")
+ assert(@compile.collections.include?(coll), "collection was not added")
# And try to collect the virtual resources.
ret = nil
@@ -148,7 +149,7 @@ class TestCollector < Test::Unit::TestCase
coll = Puppet::Parser::Collector.new(@scope, "file", nil, nil, :virtual)
end
- @scope.newcollection(coll)
+ @compile.add_collection(coll)
# run the collection and make sure it doesn't get deleted, since it
# didn't return anything
@@ -157,12 +158,12 @@ class TestCollector < Test::Unit::TestCase
"Evaluate returned incorrect value")
end
- assert_equal([coll], @scope.collections, "Collection was deleted")
+ assert_equal([coll], @compile.collections, "Collection was deleted")
# Make a resource
one = mkresource(:type => "file", :title => "/tmp/virtual1",
:virtual => true, :params => {:owner => "root"})
- @scope.setresource one
+ @scope.compile.store_resource @scope, one
# Now perform the collection again, and it should still be there
assert_nothing_raised do
@@ -170,10 +171,8 @@ class TestCollector < Test::Unit::TestCase
"Evaluate returned incorrect value")
end
- assert_equal([coll], @scope.collections, "Collection was deleted")
+ assert_equal([coll], @compile.collections, "Collection was deleted")
assert_equal(false, one.virtual?, "One was not realized")
end
end
-
-# $Id$
diff --git a/test/language/compile.rb b/test/language/compile.rb
new file mode 100755
index 000000000..5732acba3
--- /dev/null
+++ b/test/language/compile.rb
@@ -0,0 +1,669 @@
+#!/usr/bin/env ruby
+
+$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
+
+require 'mocha'
+require 'puppettest'
+require 'puppettest/parsertesting'
+require 'puppet/parser/compile'
+
+# Test our compile object.
+class TestCompile < Test::Unit::TestCase
+ include PuppetTest
+ include PuppetTest::ParserTesting
+
+ Compile = Puppet::Parser::Compile
+ Scope = Puppet::Parser::Scope
+ Node = Puppet::Network::Handler.handler(:node)
+ SimpleNode = Puppet::Node
+
+ def mknode(name = "foo")
+ @node = SimpleNode.new(name)
+ end
+
+ def mkparser
+ # This should mock an interpreter
+ @parser = stub 'parser', :version => "1.0"
+ end
+
+ def mkcompile(options = {})
+ if node = options[:node]
+ options.delete(:node)
+ else
+ node = mknode
+ end
+ @compile = Compile.new(node, mkparser, options)
+ end
+
+ def test_initialize
+ compile = nil
+ node = stub 'node', :name => "foo"
+ parser = stub 'parser', :version => "1.0"
+ assert_nothing_raised("Could not init compile with all required options") do
+ compile = Compile.new(node, parser)
+ end
+
+ assert_equal(node, compile.node, "Did not set node correctly")
+ assert_equal(parser, compile.parser, "Did not set parser correctly")
+
+ # We're not testing here whether we call initvars, because it's too difficult to
+ # mock.
+
+ # Now try it with some options
+ assert_nothing_raised("Could not init compile with extra options") do
+ compile = Compile.new(node, parser, :ast_nodes => false)
+ end
+
+ assert_equal(false, compile.ast_nodes?, "Did not set ast_nodes? correctly")
+ end
+
+ def test_initvars
+ compile = mkcompile
+ [:class_scopes, :resource_table, :exported_resources, :resource_overrides].each do |table|
+ assert_instance_of(Hash, compile.send(:instance_variable_get, "@#{table}"), "Did not set %s table correctly" % table)
+ end
+ assert_instance_of(Scope, compile.topscope, "Did not create a topscope")
+ graph = compile.instance_variable_get("@scope_graph")
+ assert_instance_of(GRATR::Digraph, graph, "Did not create scope graph")
+ assert(graph.vertex?(compile.topscope), "Did not add top scope as a vertex in the graph")
+ end
+
+ # Make sure we store and can retrieve references to classes and their scopes.
+ def test_class_set_and_class_scope
+ klass = mock 'ast_class'
+ klass.expects(:classname).returns("myname")
+
+ compile = mkcompile
+ compile.configuration.expects(:tag).with("myname")
+
+ assert_nothing_raised("Could not set class") do
+ compile.class_set "myname", "myscope"
+ end
+ # First try to retrieve it by name.
+ assert_equal("myscope", compile.class_scope("myname"), "Could not retrieve class scope by name")
+
+ # Then by object
+ assert_equal("myscope", compile.class_scope(klass), "Could not retrieve class scope by object")
+ end
+
+ def test_classlist
+ compile = mkcompile
+
+ compile.class_set "", "empty"
+ compile.class_set "one", "yep"
+ compile.class_set "two", "nope"
+
+ # Make sure our class list is correct
+ assert_equal(%w{one two}.sort, compile.classlist.sort, "Did not get correct class list")
+ end
+
+ # Make sure collections get added to our internal array
+ def test_add_collection
+ compile = mkcompile
+ assert_nothing_raised("Could not add collection") do
+ compile.add_collection "nope"
+ end
+ assert_equal(%w{nope}, compile.instance_variable_get("@collections"), "Did not add collection")
+ end
+
+ # Make sure we create a graph of scopes.
+ def test_newscope
+ compile = mkcompile
+ graph = compile.instance_variable_get("@scope_graph")
+ assert_instance_of(Scope, compile.topscope, "Did not create top scope")
+ assert_instance_of(GRATR::Digraph, graph, "Did not create graph")
+
+ assert(graph.vertex?(compile.topscope), "The top scope is not a vertex in the graph")
+
+ # Now that we've got the top scope, create a new, subscope
+ subscope = nil
+ assert_nothing_raised("Could not create subscope") do
+ subscope = compile.newscope(compile.topscope)
+ end
+ assert_instance_of(Scope, subscope, "Did not create subscope")
+ assert(graph.edge?(compile.topscope, subscope), "An edge between top scope and subscope was not added")
+
+ # Make sure a scope can find its parent.
+ assert(compile.parent(subscope), "Could not look up parent scope on compile")
+ assert_equal(compile.topscope.object_id, compile.parent(subscope).object_id, "Did not get correct parent scope from compile")
+ assert_equal(compile.topscope.object_id, subscope.parent.object_id, "Scope did not correctly retrieve its parent scope")
+
+ # Now create another, this time specifying options
+ another = nil
+ assert_nothing_raised("Could not create subscope") do
+ another = compile.newscope(subscope, :level => 5)
+ end
+ assert_equal(5, another.level, "did not set scope option correctly")
+ assert_instance_of(Scope, another, "Did not create second subscope")
+ assert(graph.edge?(subscope, another), "An edge between parent scope and second subscope was not added")
+
+ # Make sure it can find its parent.
+ assert(compile.parent(another), "Could not look up parent scope of second subscope on compile")
+ assert_equal(subscope.object_id, compile.parent(another).object_id, "Did not get correct parent scope of second subscope from compile")
+ assert_equal(subscope.object_id, another.parent.object_id, "Second subscope did not correctly retrieve its parent scope")
+
+ # And make sure both scopes show up in the right order in the search path
+ assert_equal([another.object_id, subscope.object_id, compile.topscope.object_id], another.scope_path.collect { |p| p.object_id },
+ "Did not get correct scope path")
+ end
+
+ # The heart of the action.
+ def test_compile
+ compile = mkcompile
+ [:set_node_parameters, :evaluate_main, :evaluate_ast_node, :evaluate_node_classes, :evaluate_generators, :fail_on_unevaluated, :finish].each do |method|
+ compile.expects(method)
+ end
+ assert_instance_of(Puppet::Node::Configuration, compile.compile, "Did not return the configuration")
+ end
+
+ # Test setting the node's parameters into the top scope.
+ def test_set_node_parameters
+ compile = mkcompile
+ @node.parameters = {"a" => "b", "c" => "d"}
+ scope = compile.topscope
+ @node.parameters.each do |param, value|
+ scope.expects(:setvar).with(param, value)
+ end
+
+ assert_nothing_raised("Could not call 'set_node_parameters'") do
+ compile.send(:set_node_parameters)
+ end
+ end
+
+ # Test that we can evaluate the main class, which is the one named "" in namespace
+ # "".
+ def test_evaluate_main
+ compile = mkcompile
+ main = mock 'main_class'
+ compile.topscope.expects(:source=).with(main)
+ @parser.expects(:findclass).with("", "").returns(main)
+
+ assert_nothing_raised("Could not call evaluate_main") do
+ compile.send(:evaluate_main)
+ end
+
+ assert(compile.resources.find { |r| r.to_s == "Class[main]" }, "Did not create a 'main' resource")
+ end
+
+ # Make sure we either don't look for nodes, or that we find and evaluate the right object.
+ def test_evaluate_ast_node
+ # First try it with ast_nodes disabled
+ compile = mkcompile :ast_nodes => false
+ name = compile.node.name
+ compile.expects(:ast_nodes?).returns(false)
+ compile.parser.expects(:nodes).never
+
+ assert_nothing_raised("Could not call evaluate_ast_node when ast nodes are disabled") do
+ compile.send(:evaluate_ast_node)
+ end
+
+ assert_nil(compile.resources.find { |r| r.to_s == "Node[#{name}]" }, "Created node object when ast_nodes was false")
+
+ # Now try it with them enabled, but no node found.
+ nodes = mock 'node_hash'
+ compile = mkcompile :ast_nodes => true
+ name = compile.node.name
+ compile.expects(:ast_nodes?).returns(true)
+ compile.parser.stubs(:nodes).returns(nodes)
+
+ # Set some names for our test
+ @node.names = %w{a b c}
+ nodes.expects(:[]).with("a").returns(nil)
+ nodes.expects(:[]).with("b").returns(nil)
+ nodes.expects(:[]).with("c").returns(nil)
+
+ # It should check this last, of course.
+ nodes.expects(:[]).with("default").returns(nil)
+
+ # And make sure the lack of a node throws an exception
+ assert_raise(Puppet::ParseError, "Did not fail when we couldn't find an ast node") do
+ compile.send(:evaluate_ast_node)
+ end
+
+ # Finally, make sure it works dandily when we have a node
+ compile = mkcompile :ast_nodes => true
+ compile.expects(:ast_nodes?).returns(true)
+
+ node = stub 'node', :classname => "c"
+ nodes = {"c" => node}
+ compile.parser.stubs(:nodes).returns(nodes)
+
+ # Set some names for our test
+ @node.names = %w{a b c}
+
+ # And make sure we throw no exceptions.
+ assert_nothing_raised("Failed when a node was found") do
+ compile.send(:evaluate_ast_node)
+ end
+
+ assert_instance_of(Puppet::Parser::Resource, compile.resources.find { |r| r.to_s == "Node[c]" },
+ "Did not create node resource")
+
+ # Lastly, check when we actually find the default.
+ compile = mkcompile :ast_nodes => true
+ compile.expects(:ast_nodes?).returns(true)
+
+ node = stub 'node', :classname => "default"
+ nodes = {"default" => node}
+ compile.parser.stubs(:nodes).returns(nodes)
+
+ # Set some names for our test
+ @node.names = %w{a b c}
+
+ # And make sure the lack of a node throws an exception
+ assert_nothing_raised("Failed when a node was found") do
+ compile.send(:evaluate_ast_node)
+ end
+ assert_instance_of(Puppet::Parser::Resource, compile.resources.find { |r| r.to_s == "Node[default]" },
+ "Did not create default node resource")
+ end
+
+ def test_evaluate_node_classes
+ compile = mkcompile
+ @node.classes = %w{one two three four}
+ compile.expects(:evaluate_classes).with(%w{one two three four}, compile.topscope)
+ assert_nothing_raised("could not call evaluate_node_classes") do
+ compile.send(:evaluate_node_classes)
+ end
+ end
+
+ def test_evaluate_classes
+ compile = mkcompile
+ compile.parser.expects(:findclass).with("", "").returns(stub('main', :classname => ""))
+ compile.send :evaluate_main
+ classes = {
+ "one" => stub('class one', :classname => "one"),
+ "three" => stub('class three', :classname => "three")
+ }
+
+ classes.each do |name, obj|
+ compile.parser.expects(:findclass).with("", name).returns(obj)
+ end
+ %w{two four}.each do |name|
+ compile.parser.expects(:findclass).with("", name).returns(nil)
+ end
+
+ %w{one two three four}.each do |name|
+ compile.configuration.expects(:tag).with(name)
+ end
+
+ result = nil
+ assert_nothing_raised("could not call evaluate_node_classes") do
+ result = compile.send(:evaluate_classes, %w{one two three four}, compile.topscope)
+ end
+ %w{one three}.each do |found|
+ assert(compile.resources.find { |r| r.to_s == "Class[#{found}]" }, "Did not create a class resource for %s" % found)
+ end
+ assert_equal(%w{one three}, result, "Did not return the list of evaluated classes")
+ end
+
+ def test_evaluate_collections
+ compile = mkcompile
+
+ colls = []
+
+ # Make sure we return false when there's nothing there.
+ assert(! compile.send(:evaluate_collections), "Returned true when there were no collections")
+
+ # And when the collections fail to evaluate.
+ colls << mock("coll1-false")
+ colls << mock("coll2-false")
+ colls.each { |c| c.expects(:evaluate).returns(false) }
+
+ compile.instance_variable_set("@collections", colls)
+ assert(! compile.send(:evaluate_collections), "Returned true when collections both evaluated nothing")
+
+ # Now have one of the colls evaluate
+ colls.clear
+ colls << mock("coll1-one-true")
+ colls << mock("coll2-one-true")
+ colls[0].expects(:evaluate).returns(true)
+ colls[1].expects(:evaluate).returns(false)
+ assert(compile.send(:evaluate_collections), "Did not return true when one collection evaluated true")
+
+ # And have them both eval true
+ colls.clear
+ colls << mock("coll1-both-true")
+ colls << mock("coll2-both-true")
+ colls[0].expects(:evaluate).returns(true)
+ colls[1].expects(:evaluate).returns(true)
+ assert(compile.send(:evaluate_collections), "Did not return true when both collections evaluated true")
+ end
+
+ def test_unevaluated_resources
+ compile = mkcompile
+ resources = {}
+ compile.instance_variable_set("@resource_table", resources)
+
+ # First test it when the table is empty
+ assert_nil(compile.send(:unevaluated_resources), "Somehow found unevaluated resources in an empty table")
+
+ # Then add a builtin resources
+ resources["one"] = mock("builtin only")
+ resources["one"].expects(:builtin?).returns(true)
+ assert_nil(compile.send(:unevaluated_resources), "Considered a builtin resource unevaluated")
+
+ # And do both builtin and non-builtin but already evaluated
+ resources.clear
+ resources["one"] = mock("builtin (with eval)")
+ resources["one"].expects(:builtin?).returns(true)
+ resources["two"] = mock("evaled (with builtin)")
+ resources["two"].expects(:builtin?).returns(false)
+ resources["two"].expects(:evaluated?).returns(true)
+ assert_nil(compile.send(:unevaluated_resources), "Considered either a builtin or evaluated resource unevaluated")
+
+ # Now a single unevaluated resource.
+ resources.clear
+ resources["one"] = mock("unevaluated")
+ resources["one"].expects(:builtin?).returns(false)
+ resources["one"].expects(:evaluated?).returns(false)
+ assert_equal([resources["one"]], compile.send(:unevaluated_resources), "Did not find unevaluated resource")
+
+ # With two uneval'ed resources, and an eval'ed one thrown in
+ resources.clear
+ resources["one"] = mock("unevaluated one")
+ resources["one"].expects(:builtin?).returns(false)
+ resources["one"].expects(:evaluated?).returns(false)
+ resources["two"] = mock("unevaluated two")
+ resources["two"].expects(:builtin?).returns(false)
+ resources["two"].expects(:evaluated?).returns(false)
+ resources["three"] = mock("evaluated")
+ resources["three"].expects(:builtin?).returns(false)
+ resources["three"].expects(:evaluated?).returns(true)
+
+ result = compile.send(:unevaluated_resources)
+ %w{one two}.each do |name|
+ assert(result.include?(resources[name]), "Did not find %s in the unevaluated list" % name)
+ end
+ end
+
+ def test_evaluate_definitions
+ # First try the case where there's nothing to return
+ compile = mkcompile
+ compile.expects(:unevaluated_resources).returns(nil)
+
+ assert_nothing_raised("Could not test for unevaluated resources") do
+ assert(! compile.send(:evaluate_definitions), "evaluate_definitions returned true when no resources were evaluated")
+ end
+
+ # Now try it with resources left to evaluate
+ resources = []
+ res1 = mock("resource1")
+ res1.expects(:evaluate)
+ res2 = mock("resource2")
+ res2.expects(:evaluate)
+ resources << res1 << res2
+ compile = mkcompile
+ compile.expects(:unevaluated_resources).returns(resources)
+
+ assert_nothing_raised("Could not test for unevaluated resources") do
+ assert(compile.send(:evaluate_definitions), "evaluate_definitions returned false when resources were evaluated")
+ end
+ end
+
+ def test_evaluate_generators
+ # First try the case where we have nothing to do
+ compile = mkcompile
+ compile.expects(:evaluate_definitions).returns(false)
+ compile.expects(:evaluate_collections).returns(false)
+
+ assert_nothing_raised("Could not call :eval_iterate") do
+ compile.send(:evaluate_generators)
+ end
+
+ # FIXME I could not get this test to work, but the code is short
+ # enough that I'm ok with it.
+ # It's important that collections are evaluated before definitions,
+ # so make sure that's the case by verifying that collections get tested
+ # twice but definitions only once.
+ #compile = mkcompile
+ #compile.expects(:evaluate_collections).returns(true).returns(false)
+ #compile.expects(:evaluate_definitions).returns(false)
+ #compile.send(:eval_iterate)
+ end
+
+ def test_store
+ compile = mkcompile
+ Puppet.features.expects(:rails?).returns(true)
+ Puppet::Rails.expects(:connect)
+
+ node = mock 'node'
+ resource_table = mock 'resources'
+ resource_table.expects(:values).returns(:resources)
+ compile.instance_variable_set("@node", node)
+ compile.instance_variable_set("@resource_table", resource_table)
+ compile.expects(:store_to_active_record).with(node, :resources)
+ compile.send(:store)
+ end
+
+ def test_store_to_active_record
+ compile = mkcompile
+ node = mock 'node'
+ node.expects(:name).returns("myname")
+ Puppet::Rails::Host.stubs(:transaction).yields
+ Puppet::Rails::Host.expects(:store).with(node, :resources)
+ compile.send(:store_to_active_record, node, :resources)
+ end
+
+ # Make sure that 'finish' gets called on all of our resources.
+ def test_finish
+ compile = mkcompile
+ table = compile.instance_variable_get("@resource_table")
+
+ # Add a resource that does respond to :finish
+ yep = mock("finisher")
+ yep.expects(:respond_to?).with(:finish).returns(true)
+ yep.expects(:finish)
+ table["yep"] = yep
+
+ # And one that does not
+ dnf = mock("dnf")
+ dnf.expects(:respond_to?).with(:finish).returns(false)
+ table["dnf"] = dnf
+
+ compile.send(:finish)
+ end
+
+ def test_verify_uniqueness
+ compile = mkcompile
+
+ resources = compile.instance_variable_get("@resource_table")
+ resource = mock("noconflict")
+ resource.expects(:ref).returns("File[yay]")
+ assert_nothing_raised("Raised an exception when there should have been no conflict") do
+ compile.send(:verify_uniqueness, resource)
+ end
+
+ # Now try the case where our type is isomorphic
+ resources["thing"] = true
+
+ isoconflict = mock("isoconflict")
+ isoconflict.expects(:ref).returns("thing")
+ isoconflict.expects(:type).returns("testtype")
+ faketype = mock("faketype")
+ faketype.expects(:isomorphic?).returns(false)
+ faketype.expects(:name).returns("whatever")
+ Puppet::Type.expects(:type).with("testtype").returns(faketype)
+ assert_nothing_raised("Raised an exception when was a conflict in non-isomorphic types") do
+ compile.send(:verify_uniqueness, isoconflict)
+ end
+
+ # Now test for when we actually have an exception
+ initial = mock("initial")
+ resources["thing"] = initial
+ initial.expects(:file).returns(false)
+
+ conflict = mock("conflict")
+ conflict.expects(:ref).returns("thing").times(2)
+ conflict.expects(:type).returns("conflict")
+ conflict.expects(:file).returns(false)
+ conflict.expects(:line).returns(false)
+
+ faketype = mock("faketype")
+ faketype.expects(:isomorphic?).returns(true)
+ Puppet::Type.expects(:type).with("conflict").returns(faketype)
+ assert_raise(Puppet::ParseError, "Did not fail when two isomorphic resources conflicted") do
+ compile.send(:verify_uniqueness, conflict)
+ end
+ end
+
+ def test_store_resource
+ # Run once when there's no conflict
+ compile = mkcompile
+ table = compile.instance_variable_get("@resource_table")
+ resource = mock("resource")
+ resource.expects(:ref).returns("yay")
+ compile.expects(:verify_uniqueness).with(resource)
+ scope = stub("scope", :resource => mock('resource'))
+
+ compile.configuration.expects(:add_edge!).with(scope.resource, resource)
+
+ assert_nothing_raised("Could not store resource") do
+ compile.store_resource(scope, resource)
+ end
+ assert_equal(resource, table["yay"], "Did not store resource in table")
+
+ # Now for conflicts
+ compile = mkcompile
+ table = compile.instance_variable_get("@resource_table")
+ resource = mock("resource")
+ compile.expects(:verify_uniqueness).with(resource).raises(ArgumentError)
+
+ assert_raise(ArgumentError, "Did not raise uniqueness exception") do
+ compile.store_resource(scope, resource)
+ end
+ assert(table.empty?, "Conflicting resource was stored in table")
+ end
+
+ def test_fail_on_unevaluated
+ compile = mkcompile
+ compile.expects(:fail_on_unevaluated_overrides)
+ compile.expects(:fail_on_unevaluated_resource_collections)
+ compile.send :fail_on_unevaluated
+ end
+
+ def test_store_override
+ # First test the case when the resource is not present.
+ compile = mkcompile
+ overrides = compile.instance_variable_get("@resource_overrides")
+ override = Object.new
+ override.expects(:ref).returns(:myref).times(2)
+ override.expects(:override=).with(true)
+
+ assert_nothing_raised("Could not call store_override") do
+ compile.store_override(override)
+ end
+ assert_instance_of(Array, overrides[:myref], "Overrides table is not a hash of arrays")
+ assert_equal(override, overrides[:myref][0], "Did not store override in appropriately named array")
+
+ # And when the resource already exists.
+ resource = mock 'resource'
+ resources = compile.instance_variable_get("@resource_table")
+ resources[:resref] = resource
+
+ override = mock 'override'
+ resource.expects(:merge).with(override)
+ override.expects(:override=).with(true)
+ override.expects(:ref).returns(:resref)
+ assert_nothing_raised("Could not call store_override when the resource already exists.") do
+ compile.store_override(override)
+ end
+ end
+
+ def test_resource_overrides
+ compile = mkcompile
+ overrides = compile.instance_variable_get("@resource_overrides")
+ overrides[:test] = :yay
+ resource = mock 'resource'
+ resource.expects(:ref).returns(:test)
+
+ assert_equal(:yay, compile.resource_overrides(resource), "Did not return overrides from table")
+ end
+
+ def test_fail_on_unevaluated_resource_collections
+ compile = mkcompile
+ collections = compile.instance_variable_get("@collections")
+
+ # Make sure we're fine when the list is empty
+ assert_nothing_raised("Failed when no collections were present") do
+ compile.send :fail_on_unevaluated_resource_collections
+ end
+
+ # And that we're fine when we've got collections but with no resources
+ collections << mock('coll')
+ collections[0].expects(:resources).returns(nil)
+ assert_nothing_raised("Failed when no resource collections were present") do
+ compile.send :fail_on_unevaluated_resource_collections
+ end
+
+ # But that we do fail when we've got resource collections left.
+ collections.clear
+
+ # return both an array and a string, because that's tested internally
+ collections << mock('coll returns one')
+ collections[0].expects(:resources).returns(:something)
+
+ collections << mock('coll returns many')
+ collections[1].expects(:resources).returns([:one, :two])
+
+ assert_raise(Puppet::ParseError, "Did not fail on unevaluated resource collections") do
+ compile.send :fail_on_unevaluated_resource_collections
+ end
+ end
+
+ def test_fail_on_unevaluated_overrides
+ compile = mkcompile
+ overrides = compile.instance_variable_get("@resource_overrides")
+
+ # Make sure we're fine when the list is empty
+ assert_nothing_raised("Failed when no collections were present") do
+ compile.send :fail_on_unevaluated_overrides
+ end
+
+ # But that we fail if there are any overrides left in the table.
+ overrides[:yay] = []
+ overrides[:foo] = []
+ overrides[:bar] = [mock("override")]
+ overrides[:bar][0].expects(:ref).returns("yay")
+ assert_raise(Puppet::ParseError, "Failed to fail when overrides remain") do
+ compile.send :fail_on_unevaluated_overrides
+ end
+ end
+
+ def test_find_resource
+ compile = mkcompile
+ resources = compile.instance_variable_get("@resource_table")
+
+ assert_nothing_raised("Could not call findresource when the resource table was empty") do
+ assert_nil(compile.findresource("yay", "foo"), "Returned a non-existent resource")
+ assert_nil(compile.findresource("yay[foo]"), "Returned a non-existent resource")
+ end
+
+ resources["Foo[bar]"] = :yay
+ assert_nothing_raised("Could not call findresource when the resource table was not empty") do
+ assert_equal(:yay, compile.findresource("foo", "bar"), "Returned a non-existent resource")
+ assert_equal(:yay, compile.findresource("Foo[bar]"), "Returned a non-existent resource")
+ end
+ end
+
+ # #620 - Nodes and classes should conflict, else classes don't get evaluated
+ def test_nodes_and_classes_name_conflict
+ # Test node then class
+ compile = mkcompile
+ node = stub :nodescope? => true
+ klass = stub :nodescope? => false
+ compile.class_set("one", node)
+ assert_raise(Puppet::ParseError, "Did not fail when replacing node with class") do
+ compile.class_set("one", klass)
+ end
+
+ # and class then node
+ compile = mkcompile
+ node = stub :nodescope? => true
+ klass = stub :nodescope? => false
+ compile.class_set("two", klass)
+ assert_raise(Puppet::ParseError, "Did not fail when replacing node with class") do
+ compile.class_set("two", node)
+ end
+ end
+end
diff --git a/test/language/configuration.rb b/test/language/configuration.rb
deleted file mode 100755
index 4cbba8063..000000000
--- a/test/language/configuration.rb
+++ /dev/null
@@ -1,131 +0,0 @@
-#!/usr/bin/env ruby
-
-$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
-
-require 'mocha'
-require 'puppettest'
-require 'puppettest/parsertesting'
-require 'puppet/parser/configuration'
-
-# Test our configuration object.
-class TestConfiguration < Test::Unit::TestCase
- include PuppetTest
- include PuppetTest::ParserTesting
-
- Config = Puppet::Parser::Configuration
- Scope = Puppet::Parser::Scope
-
- def mkconfig
- Config.new(:host => "foo", :interpreter => "interp")
- end
-
- def test_initialize
- # Make sure we get an error if we don't send an interpreter
- assert_raise(ArgumentError, "Did not fail when missing host") do
- Config.new(:interpreter => "yay" )
- end
- assert_raise(ArgumentError, "Did not fail when missing interp") do
- Config.new(:host => "foo")
- end
-
- # Now check the defaults
- config = nil
- assert_nothing_raised("Could not init config with all required options") do
- config = Config.new(:host => "foo", :interpreter => "interp")
- end
-
- assert_equal("foo", config.host, "Did not set host correctly")
- assert_equal("interp", config.interpreter, "Did not set interpreter correctly")
- assert_equal({}, config.facts, "Did not set default facts")
-
- # Now make a new one with facts, to make sure the facts get set appropriately
- assert_nothing_raised("Could not init config with all required options") do
- config = Config.new(:host => "foo", :interpreter => "interp", :facts => {"a" => "b"})
- end
- assert_equal({"a" => "b"}, config.facts, "Did not set facts")
- end
-
- def test_initvars
- config = mkconfig
- [:class_scopes, :resource_table, :exported_resources, :resource_overrides].each do |table|
- assert_instance_of(Hash, config.send(:instance_variable_get, "@#{table}"), "Did not set %s table correctly" % table)
- end
- end
-
- # Make sure we store and can retrieve references to classes and their scopes.
- def test_class_set_and_class_scope
- klass = Object.new
- klass.expects(:classname).returns("myname")
-
- config = mkconfig
-
- assert_nothing_raised("Could not set class") do
- config.class_set "myname", "myscope"
- end
- # First try to retrieve it by name.
- assert_equal("myscope", config.class_scope("myname"), "Could not retrieve class scope by name")
-
- # Then by object
- assert_equal("myscope", config.class_scope(klass), "Could not retrieve class scope by object")
- end
-
- def test_classlist
- config = mkconfig
-
- config.class_set "", "empty"
- config.class_set "one", "yep"
- config.class_set "two", "nope"
-
- # Make sure our class list is correct
- assert_equal(%w{one two}.sort, config.classlist.sort, "Did not get correct class list")
- end
-
- # Make sure collections get added to our internal array
- def test_add_collection
- config = mkconfig
- assert_nothing_raised("Could not add collection") do
- config.add_collection "nope"
- end
- assert_equal(%w{nope}, config.instance_variable_get("@collections"), "Did not add collection")
- end
-
- # Make sure we create a graph of scopes.
- def test_newscope
- config = mkconfig
- graph = config.instance_variable_get("@graph")
- assert_instance_of(Scope, config.topscope, "Did not create top scope")
- assert_instance_of(GRATR::Digraph, graph, "Did not create graph")
-
- assert(graph.vertex?(config.topscope), "The top scope is not a vertex in the graph")
-
- # Now that we've got the top scope, create a new, subscope
- subscope = nil
- assert_nothing_raised("Could not create subscope") do
- subscope = config.newscope
- end
- assert_instance_of(Scope, subscope, "Did not create subscope")
- assert(graph.edge?(config.topscope, subscope), "An edge between top scope and subscope was not added")
-
- # Make sure a scope can find its parent.
- assert(config.parent(subscope), "Could not look up parent scope on configuration")
- assert_equal(config.topscope.object_id, config.parent(subscope).object_id, "Did not get correct parent scope from configuration")
- assert_equal(config.topscope.object_id, subscope.parent.object_id, "Scope did not correctly retrieve its parent scope")
-
- # Now create another, this time specifying the parent scope
- another = nil
- assert_nothing_raised("Could not create subscope") do
- another = config.newscope(subscope)
- end
- assert_instance_of(Scope, another, "Did not create second subscope")
- assert(graph.edge?(subscope, another), "An edge between parent scope and second subscope was not added")
-
- # Make sure it can find its parent.
- assert(config.parent(another), "Could not look up parent scope of second subscope on configuration")
- assert_equal(subscope.object_id, config.parent(another).object_id, "Did not get correct parent scope of second subscope from configuration")
- assert_equal(subscope.object_id, another.parent.object_id, "Second subscope did not correctly retrieve its parent scope")
-
- # And make sure both scopes show up in the right order in the search path
- assert_equal([another.object_id, subscope.object_id, config.topscope.object_id], another.scope_path.collect { |p| p.object_id },
- "Did not get correct scope path")
- end
-end
diff --git a/test/language/functions.rb b/test/language/functions.rb
index 34207de17..be06d8eac 100755
--- a/test/language/functions.rb
+++ b/test/language/functions.rb
@@ -3,7 +3,6 @@
$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
require 'puppet'
-require 'puppet/parser/interpreter'
require 'puppet/parser/parser'
require 'puppet/network/client'
require 'puppettest'
@@ -50,10 +49,9 @@ class TestLangFunctions < Test::Unit::TestCase
def test_taggedfunction
scope = mkscope
+ scope.resource.tag("yayness")
- tag = "yayness"
- scope.tag(tag)
-
+ # Make sure the ast stuff does what it's supposed to
{"yayness" => true, "booness" => false}.each do |tag, retval|
func = taggedobj(tag, :rvalue)
@@ -64,6 +62,12 @@ class TestLangFunctions < Test::Unit::TestCase
assert_equal(retval, val, "'tagged' returned %s for %s" % [val, tag])
end
+
+ # Now make sure we correctly get tags.
+ scope.resource.tag("resourcetag")
+ assert(scope.function_tagged("resourcetag"), "tagged function did not catch resource tags")
+ scope.compile.configuration.tag("configtag")
+ assert(scope.function_tagged("configtag"), "tagged function did not catch configuration tags")
end
def test_failfunction
@@ -204,24 +208,29 @@ class TestLangFunctions < Test::Unit::TestCase
f.puts %{file { "#{file}": content => template("#{template}") }}
end
- interpreter = Puppet::Parser::Interpreter.new(
+ interp = Puppet::Parser::Interpreter.new(
:Manifest => manifest,
:UseNodes => false
)
+ node = mknode
+ node.stubs(:environment).returns("yay")
- parsedate = interpreter.parsedate()
+ Puppet[:environment] = "yay"
- objects = nil
+ configuration = nil
assert_nothing_raised {
- objects = interpreter.run("myhost", {})
+ configuration = interp.compile(node)
}
- fileobj = objects[0]
+ version = configuration.version
+
+ fileobj = configuration.vertices.find { |r| r.title == file }
+ assert(fileobj, "File was not in configuration")
assert_equal("original text\n", fileobj["content"],
"Template did not work")
- Puppet[:filetimeout] = 0
+ Puppet[:filetimeout] = -5
# Have to sleep because one second is the fs's time granularity.
sleep(1)
@@ -230,12 +239,9 @@ class TestLangFunctions < Test::Unit::TestCase
f.puts "new text"
end
- assert_nothing_raised {
- objects = interpreter.run("myhost", {})
- }
- newdate = interpreter.parsedate()
+ newversion = interp.compile(node).version
- assert(parsedate != newdate, "Parse date did not change")
+ assert(version != newversion, "Parse date did not change")
end
def test_template_defined_vars
@@ -306,35 +312,36 @@ class TestLangFunctions < Test::Unit::TestCase
end
def test_realize
- @interp, @scope, @source = mkclassframing
+ scope = mkscope
+ parser = scope.compile.parser
# Make a definition
- @interp.newdefine("mytype")
+ parser.newdefine("mytype")
[%w{file /tmp/virtual}, %w{mytype yay}].each do |type, title|
# Make a virtual resource
virtual = mkresource(:type => type, :title => title,
- :virtual => true, :params => {})
+ :virtual => true, :params => {}, :scope => scope)
- @scope.setresource virtual
+ scope.compile.store_resource(scope, virtual)
ref = Puppet::Parser::Resource::Reference.new(
:type => type, :title => title,
- :scope => @scope
+ :scope => scope
)
# Now call the realize function
assert_nothing_raised do
- @scope.function_realize(ref)
+ scope.function_realize(ref)
end
# Make sure it created a collection
- assert_equal(1, @scope.collections.length,
+ assert_equal(1, scope.compile.collections.length,
"Did not set collection")
assert_nothing_raised do
- @scope.collections.each do |coll| coll.evaluate end
+ scope.compile.collections.each do |coll| coll.evaluate end
end
- @scope.collections.clear
+ scope.compile.collections.clear
# Now make sure the virtual resource is no longer virtual
assert(! virtual.virtual?, "Did not make virtual resource real")
@@ -343,29 +350,29 @@ class TestLangFunctions < Test::Unit::TestCase
# Make sure we puke on any resource that doesn't exist
none = Puppet::Parser::Resource::Reference.new(
:type => "file", :title => "/tmp/nosuchfile",
- :scope => @scope
+ :scope => scope
)
# The function works
assert_nothing_raised do
- @scope.function_realize(none.to_s)
+ scope.function_realize(none.to_s)
end
# Make sure it created a collection
- assert_equal(1, @scope.collections.length,
+ assert_equal(1, scope.compile.collections.length,
"Did not set collection")
# And the collection has our resource in it
- assert_equal([none.to_s], @scope.collections[0].resources,
+ assert_equal([none.to_s], scope.compile.collections[0].resources,
"Did not set resources in collection")
end
def test_defined
- interp = mkinterp
- scope = mkscope(:interp => interp)
+ scope = mkscope
+ parser = scope.compile.parser
- interp.newclass("yayness")
- interp.newdefine("rahness")
+ parser.newclass("yayness")
+ parser.newdefine("rahness")
assert_nothing_raised do
assert(scope.function_defined("yayness"), "yayness class was not considered defined")
@@ -383,9 +390,9 @@ class TestLangFunctions < Test::Unit::TestCase
"Multiple falses were somehow true")
# Now make sure we can test resources
- scope.setresource mkresource(:type => "file", :title => "/tmp/rahness",
+ scope.compile.store_resource(scope, mkresource(:type => "file", :title => "/tmp/rahness",
:scope => scope, :source => scope.source,
- :params => {:owner => "root"})
+ :params => {:owner => "root"}))
yep = Puppet::Parser::Resource::Reference.new(:type => "file", :title => "/tmp/rahness")
nope = Puppet::Parser::Resource::Reference.new(:type => "file", :title => "/tmp/fooness")
@@ -395,11 +402,11 @@ class TestLangFunctions < Test::Unit::TestCase
end
def test_search
- interp = mkinterp
- scope = mkscope(:interp => interp)
+ parser = mkparser
+ scope = mkscope(:parser => parser)
- fun = interp.newdefine("yay::ness")
- foo = interp.newdefine("foo::bar")
+ fun = parser.newdefine("yay::ness")
+ foo = parser.newdefine("foo::bar")
search = Puppet::Parser::Functions.function(:search)
assert_nothing_raised do
@@ -417,36 +424,36 @@ class TestLangFunctions < Test::Unit::TestCase
end
def test_include
- interp = mkinterp
- scope = mkscope(:interp => interp)
+ scope = mkscope
+ parser = scope.compile.parser
assert_raise(Puppet::ParseError, "did not throw error on missing class") do
scope.function_include("nosuchclass")
end
- interp.newclass("myclass")
+ parser.newclass("myclass")
assert_nothing_raised do
scope.function_include "myclass"
end
- assert(scope.classlist.include?("myclass"),
+ assert(scope.compile.resources.find { |r| r.to_s == "Class[myclass]" },
"class was not evaluated")
# Now try multiple classes at once
- classes = %w{one two three}.each { |c| interp.newclass(c) }
+ classes = %w{one two three}.each { |c| parser.newclass(c) }
assert_nothing_raised do
scope.function_include classes
end
classes.each do |c|
- assert(scope.classlist.include?(c),
+ assert(scope.compile.resources.find { |r| r.to_s == "Class[#{c}]" },
"class %s was not evaluated" % c)
end
# Now try a scoped class
- interp.newclass("os::redhat")
+ parser.newclass("os::redhat")
assert_nothing_raised("Could not include qualified class name") do
scope.function_include("os::redhat")
@@ -454,8 +461,8 @@ class TestLangFunctions < Test::Unit::TestCase
end
def test_file
- interp = mkinterp
- scope = mkscope(:interp => interp)
+ parser = mkparser
+ scope = mkscope(:parser => parser)
file1 = tempfile
file2 = tempfile
@@ -497,8 +504,8 @@ class TestLangFunctions < Test::Unit::TestCase
assert_equal("yay\n", %x{#{command}}, "command did not work")
assert_equal("yay-foo\n", %x{#{command} foo}, "command did not work")
- interp = mkinterp
- scope = mkscope(:interp => interp)
+ scope = mkscope
+ parser = scope.compile.parser
val = nil
assert_nothing_raised("Could not call generator with no args") do
diff --git a/test/language/interpreter.rb b/test/language/interpreter.rb
deleted file mode 100755
index 070e2e77e..000000000
--- a/test/language/interpreter.rb
+++ /dev/null
@@ -1,537 +0,0 @@
-#!/usr/bin/env ruby
-
-$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
-
-require 'facter'
-
-require 'puppet'
-require 'puppet/parser/interpreter'
-require 'puppet/parser/parser'
-require 'puppet/network/client'
-require 'puppettest'
-require 'puppettest/resourcetesting'
-require 'puppettest/parsertesting'
-require 'puppettest/servertest'
-require 'timeout'
-
-class TestInterpreter < PuppetTest::TestCase
- include PuppetTest
- include PuppetTest::ServerTest
- include PuppetTest::ParserTesting
- include PuppetTest::ResourceTesting
- AST = Puppet::Parser::AST
- NodeDef = Puppet::Parser::Interpreter::NodeDef
-
- # create a simple manifest that uses nodes to create a file
- def mknodemanifest(node, file)
- createdfile = tempfile()
-
- File.open(file, "w") { |f|
- f.puts "node %s { file { \"%s\": ensure => file, mode => 755 } }\n" %
- [node, createdfile]
- }
-
- return [file, createdfile]
- end
-
- def test_simple
- file = tempfile()
- File.open(file, "w") { |f|
- f.puts "file { \"/etc\": owner => root }"
- }
- assert_nothing_raised {
- Puppet::Parser::Interpreter.new(:Manifest => file)
- }
- end
-
- def test_reloadfiles
- hostname = Facter["hostname"].value
-
- file = tempfile()
-
- # Create a first version
- createdfile = mknodemanifest(hostname, file)
-
- interp = nil
- assert_nothing_raised {
- interp = Puppet::Parser::Interpreter.new(:Manifest => file)
- }
-
- config = nil
- assert_nothing_raised {
- config = interp.run(hostname, {})
- }
- sleep(1)
-
- # Now create a new file
- createdfile = mknodemanifest(hostname, file)
-
- newconfig = nil
- assert_nothing_raised {
- newconfig = interp.run(hostname, {})
- }
-
- assert(config != newconfig, "Configs are somehow the same")
- end
-
- # Make sure searchnode behaves as we expect.
- def test_nodesearch
- # We use two sources here to catch a weird bug where the default
- # node is used if the host isn't in the first source.
- interp = mkinterp
-
- # Make some nodes
- names = %w{node1 node2 node2.domain.com}
- interp.newnode names
- interp.newnode %w{default}
-
- nodes = {}
- # Make sure we can find them all, using the direct method
- names.each do |name|
- nodes[name] = interp.nodesearch_code(name)
- assert(nodes[name], "Could not find %s" % name)
- nodes[name].file = __FILE__
- end
-
- # Now let's try it with the nodesearch method
- names.each do |name|
- node = interp.nodesearch(name)
- assert(node, "Could not find #{name} via nodesearch")
- end
-
- # Make sure we find the default node when we search for nonexistent nodes
- assert_nothing_raised do
- default = interp.nodesearch("nosuchnode")
- assert(default, "Did not find default node")
- assert_equal("default", default.classname)
- end
-
- # Now make sure the longest match always wins
- node = interp.nodesearch(*%w{node2 node2.domain.com})
-
- assert(node, "Did not find node2")
- assert_equal("node2.domain.com", node.classname,
- "Did not get longest match")
- end
-
- def test_parsedate
- Puppet[:filetimeout] = 0
- main = tempfile()
- sub = tempfile()
- mainfile = tempfile()
- subfile = tempfile()
- count = 0
- updatemain = proc do
- count += 1
- File.open(main, "w") { |f|
- f.puts "import '#{sub}'
- file { \"#{mainfile}\": content => #{count} }
- "
- }
- end
- updatesub = proc do
- count += 1
- File.open(sub, "w") { |f|
- f.puts "file { \"#{subfile}\": content => #{count} }
- "
- }
- end
-
- updatemain.call
- updatesub.call
-
- interp = Puppet::Parser::Interpreter.new(
- :Manifest => main,
- :Local => true
- )
-
- date = interp.parsedate
-
- # Now update the site file and make sure we catch it
- sleep 1
- updatemain.call
- newdate = interp.parsedate
- assert(date != newdate, "Parsedate was not updated")
- date = newdate
-
- # And then the subfile
- sleep 1
- updatesub.call
- newdate = interp.parsedate
- assert(date != newdate, "Parsedate was not updated")
- end
-
- # Make sure class, node, and define methods are case-insensitive
- def test_structure_case_insensitivity
- interp = mkinterp
-
- result = nil
- assert_nothing_raised do
- result = interp.newclass "Yayness"
- end
- assert_equal(result, interp.findclass("", "yayNess"))
-
- assert_nothing_raised do
- result = interp.newdefine "FunTest"
- end
- assert_equal(result, interp.finddefine("", "fUntEst"),
- "%s was not matched" % "fUntEst")
-
- assert_nothing_raised do
- result = interp.newnode("MyNode").shift
- end
- assert_equal(result, interp.nodesearch("mYnOde"),
- "mYnOde was not matched")
-
- assert_nothing_raised do
- result = interp.newnode("YayTest.Domain.Com").shift
- end
- assert_equal(result, interp.nodesearch("yaYtEst.domAin.cOm"),
- "yaYtEst.domAin.cOm was not matched")
- end
-
- # Make sure our whole chain works.
- def test_evaluate
- interp, scope, source = mkclassframing
-
- # Create a define that we'll be using
- interp.newdefine("wrapper", :code => AST::ASTArray.new(:children => [
- resourcedef("file", varref("name"), "owner" => "root")
- ]))
-
- # Now create a resource that uses that define
- define = mkresource(:type => "wrapper", :title => "/tmp/testing",
- :scope => scope, :source => source, :params => :none)
-
- scope.setresource define
-
- # And a normal resource
- scope.setresource mkresource(:type => "file", :title => "/tmp/rahness",
- :scope => scope, :source => source,
- :params => {:owner => "root"})
-
- # Now evaluate everything
- objects = nil
- interp.usenodes = false
- assert_nothing_raised do
- objects = interp.evaluate(nil, {})
- end
-
- assert_instance_of(Puppet::TransBucket, objects)
- end
-
- # Test evaliterate. It's a very simple method, but it's pretty tough
- # to test. It iterates over collections and instances of defined types
- # until there's no more work to do.
- def test_evaliterate
- interp, scope, source = mkclassframing
-
- # Create a top-level definition that creates a builtin object
- interp.newdefine("one", :arguments => [%w{owner}],
- :code => AST::ASTArray.new(:children => [
- resourcedef("file", varref("name"),
- "owner" => varref("owner")
- )
- ])
- )
-
- # Create another definition to call that one
- interp.newdefine("two", :arguments => [%w{owner}],
- :code => AST::ASTArray.new(:children => [
- resourcedef("one", varref("name"),
- "owner" => varref("owner")
- )
- ])
- )
-
- # And then a third
- interp.newdefine("three", :arguments => [%w{owner}],
- :code => AST::ASTArray.new(:children => [
- resourcedef("two", varref("name"),
- "owner" => varref("owner")
- )
- ])
- )
-
- # And create a definition that creates a virtual resource
- interp.newdefine("virtualizer", :arguments => [%w{owner}],
- :code => AST::ASTArray.new(:children => [
- virt_resourcedef("one", varref("name"),
- "owner" => varref("owner")
- )
- ])
- )
-
- # Now create an instance of three
- three = Puppet::Parser::Resource.new(
- :type => "three", :title => "one",
- :scope => scope, :source => source,
- :params => paramify(source, :owner => "root")
- )
- scope.setresource(three)
-
- # An instance of the virtualizer
- virt = Puppet::Parser::Resource.new(
- :type => "virtualizer", :title => "two",
- :scope => scope, :source => source,
- :params => paramify(source, :owner => "root")
- )
- scope.setresource(virt)
-
- # And a virtual instance of three
- virt_three = Puppet::Parser::Resource.new(
- :type => "three", :title => "three",
- :scope => scope, :source => source,
- :params => paramify(source, :owner => "root")
- )
- virt_three.virtual = true
- scope.setresource(virt_three)
-
- # Create a normal, virtual resource
- plainvirt = Puppet::Parser::Resource.new(
- :type => "user", :title => "five",
- :scope => scope, :source => source,
- :params => paramify(source, :uid => "root")
- )
- plainvirt.virtual = true
- scope.setresource(plainvirt)
-
- # Now create some collections for our virtual resources
- %w{Three[three] One[two]}.each do |ref|
- coll = Puppet::Parser::Collector.new(scope, "file", nil, nil, :virtual)
- coll.resources = [ref]
- scope.newcollection(coll)
- end
-
- # And create a generic user collector for our plain resource
- coll = Puppet::Parser::Collector.new(scope, "user", nil, nil, :virtual)
- scope.newcollection(coll)
-
- ret = nil
- assert_nothing_raised do
- ret = scope.unevaluated
- end
-
-
- assert_instance_of(Array, ret)
- assert_equal(3, ret.length,
- "did not get the correct number of unevaled resources")
-
- # Now translate the whole tree
- assert_nothing_raised do
- Timeout::timeout(2) do
- interp.evaliterate(scope)
- end
- end
-
- # Now make sure we've got all of our files
- %w{one two three}.each do |name|
- file = scope.findresource("File[%s]" % name)
- assert(file, "Could not find file %s" % name)
-
- assert_equal("root", file[:owner])
- assert(! file.virtual?, "file %s is still virtual" % name)
- end
-
- # Now make sure we found the user
- assert(! plainvirt.virtual?, "user was not realized")
- end
-
- # Make sure we fail if there are any leftover overrides to perform.
- # This would normally mean that someone is trying to override an object
- # that does not exist.
- def test_failonleftovers
- interp, scope, source = mkclassframing
-
- # Make sure we don't fail, since there are no overrides
- assert_nothing_raised do
- interp.failonleftovers(scope)
- end
-
- # Add an override, and make sure it causes a failure
- over1 = mkresource :scope => scope, :source => source,
- :params => {:one => "yay"}
-
- scope.setoverride(over1)
-
- assert_raise(Puppet::ParseError) do
- interp.failonleftovers(scope)
- end
-
- # Make a new scope to test leftover collections
- scope = mkscope :interp => interp
- interp.meta_def(:check_resource_collections) do
- raise ArgumentError, "yep"
- end
-
- assert_raise(ArgumentError, "did not call check_resource_colls") do
- interp.failonleftovers(scope)
- end
- end
-
- def test_evalnode
- interp = mkinterp
- interp.usenodes = false
- scope = Parser::Scope.new(:interp => interp)
- facts = Facter.to_hash
-
- # First make sure we get no failures when client is nil
- assert_nothing_raised do
- interp.evalnode(nil, scope, facts)
- end
-
- # Now define a node
- interp.newnode "mynode", :code => AST::ASTArray.new(:children => [
- resourcedef("file", "/tmp/testing", "owner" => "root")
- ])
-
- # Eval again, and make sure it does nothing
- assert_nothing_raised do
- interp.evalnode("mynode", scope, facts)
- end
-
- assert_nil(scope.findresource("File[/tmp/testing]"),
- "Eval'ed node with nodes off")
-
- # Now enable usenodes and make sure it works.
- interp.usenodes = true
- assert_nothing_raised do
- interp.evalnode("mynode", scope, facts)
- end
- file = scope.findresource("File[/tmp/testing]")
-
- assert_instance_of(Puppet::Parser::Resource, file,
- "Could not find file")
- end
-
- # This is mostly used for the cfengine module
- def test_specificclasses
- interp = mkinterp :Classes => %w{klass1 klass2}, :UseNodes => false
-
- # Make sure it's not a failure to be missing classes, since
- # we're using the cfengine class list, which is huge.
- assert_nothing_raised do
- interp.evaluate(nil, {})
- end
-
- interp.newclass("klass1", :code => AST::ASTArray.new(:children => [
- resourcedef("file", "/tmp/klass1", "owner" => "root")
- ]))
- interp.newclass("klass2", :code => AST::ASTArray.new(:children => [
- resourcedef("file", "/tmp/klass2", "owner" => "root")
- ]))
-
- ret = nil
- assert_nothing_raised do
- ret = interp.evaluate(nil, {})
- end
-
- found = ret.flatten.collect do |res| res.name end
-
- assert(found.include?("/tmp/klass1"), "Did not evaluate klass1")
- assert(found.include?("/tmp/klass2"), "Did not evaluate klass2")
- end
-
- def test_check_resource_collections
- interp = mkinterp
- scope = mkscope :interp => interp
- coll = Puppet::Parser::Collector.new(scope, "file", nil, nil, :virtual)
- coll.resources = ["File[/tmp/virtual1]", "File[/tmp/virtual2]"]
- scope.newcollection(coll)
-
- assert_raise(Puppet::ParseError, "Did not fail on remaining resource colls") do
- interp.check_resource_collections(scope)
- end
- end
-
- def test_nodedef
- interp = mkinterp
- interp.newclass("base")
- interp.newclass("sub", :parent => "base")
- interp.newclass("other")
-
- node = nil
- assert_nothing_raised("Could not create a node definition") do
- node = NodeDef.new :name => "yay", :classes => "sub", :parameters => {"one" => "two", "three" => "four"}
- end
-
- scope = mkscope :interp => interp
- assert_nothing_raised("Could not evaluate the node definition") do
- node.evaluate(:scope => scope)
- end
-
- assert_equal("two", scope.lookupvar("one"), "NodeDef did not set variable")
- assert_equal("four", scope.lookupvar("three"), "NodeDef did not set variable")
-
- assert(scope.classlist.include?("sub"), "NodeDef did not evaluate class")
- assert(scope.classlist.include?("base"), "NodeDef did not evaluate base class")
-
- # Now try a node def with multiple classes
- assert_nothing_raised("Could not create a node definition") do
- node = NodeDef.new :name => "yay", :classes => %w{sub other base}, :parameters => {"one" => "two", "three" => "four"}
- end
-
- scope = mkscope :interp => interp
- assert_nothing_raised("Could not evaluate the node definition") do
- node.evaluate(:scope => scope)
- end
-
- assert_equal("two", scope.lookupvar("one"), "NodeDef did not set variable")
- assert_equal("four", scope.lookupvar("three"), "NodeDef did not set variable")
-
- assert(scope.classlist.include?("sub"), "NodeDef did not evaluate class")
- assert(scope.classlist.include?("other"), "NodeDef did not evaluate other class")
-
- # And a node def with no params
- assert_nothing_raised("Could not create a node definition with no params") do
- node = NodeDef.new :name => "yay", :classes => %w{sub other base}
- end
-
- scope = mkscope :interp => interp
- assert_nothing_raised("Could not evaluate the node definition") do
- node.evaluate(:scope => scope)
- end
-
- assert(scope.classlist.include?("sub"), "NodeDef did not evaluate class")
- assert(scope.classlist.include?("other"), "NodeDef did not evaluate other class")
-
- # Now make sure nodedef doesn't fail when some classes are not defined (#687).
- assert_nothing_raised("Could not create a node definition with some invalid classes") do
- node = NodeDef.new :name => "yay", :classes => %w{base unknown}
- end
-
- scope = mkscope :interp => interp
- assert_nothing_raised("Could not evaluate the node definition with some invalid classes") do
- node.evaluate(:scope => scope)
- end
-
- assert(scope.classlist.include?("base"), "NodeDef did not evaluate class")
- end
-
- # Make sure that reparsing is atomic -- failures don't cause a broken state, and we aren't subject
- # to race conditions if someone contacts us while we're reparsing.
- def test_atomic_reparsing
- Puppet[:filetimeout] = -10
- file = tempfile
- File.open(file, "w") { |f| f.puts %{file { '/tmp': ensure => directory }} }
- interp = mkinterp :Manifest => file, :UseNodes => false
-
- assert_nothing_raised("Could not compile the first time") do
- interp.run("yay", {})
- end
-
- oldparser = interp.send(:instance_variable_get, "@parser")
-
- # Now add a syntax failure
- File.open(file, "w") { |f| f.puts %{file { /tmp: ensure => directory }} }
- assert_nothing_raised("Could not compile the first time") do
- interp.run("yay", {})
- end
-
- # And make sure the old parser is still there
- newparser = interp.send(:instance_variable_get, "@parser")
- assert_equal(oldparser.object_id, newparser.object_id, "Failed parser still replaced existing parser")
- end
-end
-
-# $Id$
diff --git a/test/language/parser.rb b/test/language/parser.rb
index afffa9293..1cef72dcb 100755
--- a/test/language/parser.rb
+++ b/test/language/parser.rb
@@ -38,13 +38,13 @@ class TestParser < Test::Unit::TestCase
def test_failers
failers { |file|
parser = mkparser
- interp = mkinterp
Puppet.debug("parsing failer %s" % file) if __FILE__ == $0
- assert_raise(Puppet::ParseError) {
+ assert_raise(Puppet::ParseError, "Did not fail while parsing %s" % file) {
parser.file = file
ast = parser.parse
- scope = mkscope :interp => interp
- ast.classes[""].evaluate :scope => scope
+ config = mkcompile(parser)
+ config.compile
+ #ast.classes[""].evaluate :scope => config.topscope
}
Puppet::Type.allclear
}
@@ -371,7 +371,7 @@ file { "/tmp/yayness":
ret = parser.parse(str1).classes[""].code[0]
}
assert_instance_of(Puppet::Parser::AST::IfStatement, ret)
- parser.clear
+ parser = mkparser
str2 = %{if true { #{exec.call("true")} } else { #{exec.call("false")} }}
assert_nothing_raised {
ret = parser.parse(str2).classes[""].code[0]
@@ -475,7 +475,7 @@ file { "/tmp/yayness":
assert_instance_of(AST::ASTArray, ret.classes[""].code)
resdef = ret.classes[""].code[0]
- assert_instance_of(AST::ResourceDef, resdef)
+ assert_instance_of(AST::Resource, resdef)
assert_equal("/tmp/testing", resdef.title.value)
# We always get an astarray back, so...
check.call(resdef, "simple resource")
@@ -486,7 +486,7 @@ file { "/tmp/yayness":
end
ret.classes[""].each do |res|
- assert_instance_of(AST::ResourceDef, res)
+ assert_instance_of(AST::Resource, res)
check.call(res, "multiresource")
end
@@ -497,7 +497,7 @@ file { "/tmp/yayness":
scope.source = klass
assert_nothing_raised do
- ret.classes[""].evaluate :scope => scope
+ ret.classes[""].evaluate :scope => scope, :resource => Puppet::Parser::Resource.new(:type => "mydefine", :title => 'whatever', :scope => scope, :source => scope.source)
end
# Make sure we can find both of them
@@ -622,7 +622,7 @@ file { "/tmp/yayness":
code = nil
assert_nothing_raised do
- code = interp.run("hostname.domain.com", {}).flatten
+ code = interp.compile(mknode).extract.flatten
end
assert(code.length == 1, "Did not get the file")
assert_instance_of(Puppet::TransObject, code[0])
@@ -672,7 +672,7 @@ file { "/tmp/yayness":
targets << target
txt = %[ file { '#{target}': content => "#{fname}" } ]
if fname == "init.pp"
- txt = %[import 'mani1' \nimport '#{modname}/mani2'\nimport '#{modname}/sub/*.pp' ] + txt
+ txt = %[import 'mani1' \nimport '#{modname}/mani2'\nimport '#{modname}/sub/*.pp'\n ] + txt
end
File::open(File::join(manipath, fname), "w") do |f|
f.puts txt
@@ -785,7 +785,7 @@ file { "/tmp/yayness":
assert_instance_of(AST::HostClass, result.classes["yay"], "Did not create 'yay' class")
assert_instance_of(AST::HostClass, result.classes[""], "Did not create main class")
- assert_instance_of(AST::Component, result.definitions["bar"], "Did not create 'bar' definition")
+ assert_instance_of(AST::Definition, result.definitions["bar"], "Did not create 'bar' definition")
assert_instance_of(AST::Node, result.nodes["foo"], "Did not create 'foo' node")
end
@@ -871,7 +871,8 @@ file { "/tmp/yayness":
end
def test_newclass
- parser = mkparser
+ scope = mkscope
+ parser = scope.compile.parser
mkcode = proc do |ary|
classes = ary.collect do |string|
@@ -880,7 +881,6 @@ file { "/tmp/yayness":
AST::ASTArray.new(:children => classes)
end
- scope = Puppet::Parser::Scope.new(:interp => mkinterp)
# First make sure that code is being appended
code = mkcode.call(%w{original code})
@@ -1118,7 +1118,7 @@ file { "/tmp/yayness":
mk_module(name, :define => true, :init => [name])
klass = parser.finddefine("", name)
- assert_instance_of(AST::Component, klass, "Did not autoload class from module init file")
+ assert_instance_of(AST::Definition, klass, "Did not autoload class from module init file")
assert_equal(name, klass.classname, "Incorrect class was returned")
# Now try it with namespace classes where both classes are in the init file
@@ -1175,6 +1175,23 @@ file { "/tmp/yayness":
assert_instance_of(AST::HostClass, klass, "Did not autoload sub class from alone file with no namespace")
assert_equal("alone::sub", klass.classname, "Incorrect class was returned")
end
+
+ # Make sure class, node, and define methods are case-insensitive
+ def test_structure_case_insensitivity
+ parser = mkparser
+
+ result = nil
+ assert_nothing_raised do
+ result = parser.newclass "Yayness"
+ end
+ assert_equal(result, parser.findclass("", "yayNess"))
+
+ assert_nothing_raised do
+ result = parser.newdefine "FunTest"
+ end
+ assert_equal(result, parser.finddefine("", "fUntEst"),
+ "%s was not matched" % "fUntEst")
+ end
end
# $Id$
diff --git a/test/language/resource.rb b/test/language/resource.rb
index 039c67216..f25be93b5 100755
--- a/test/language/resource.rb
+++ b/test/language/resource.rb
@@ -11,159 +11,217 @@ class TestResource < PuppetTest::TestCase
include PuppetTest::ResourceTesting
Parser = Puppet::Parser
AST = Parser::AST
+ Resource = Puppet::Parser::Resource
Reference = Puppet::Parser::Resource::Reference
def setup
super
Puppet[:trace] = false
- @interp, @scope, @source = mkclassframing
+ end
+
+ def teardown
+ mocha_verify
end
def test_initialize
args = {:type => "resource", :title => "testing",
- :source => @source, :scope => @scope}
+ :scope => mkscope}
# Check our arg requirements
args.each do |name, value|
try = args.dup
try.delete(name)
- assert_raise(Puppet::DevError) do
+ assert_raise(ArgumentError, "Did not fail when %s was missing" % name) do
Parser::Resource.new(try)
end
end
- args[:params] = paramify @source, :one => "yay", :three => "rah"
-
res = nil
assert_nothing_raised do
res = Parser::Resource.new(args)
end
- # Make sure it got the parameters correctly.
- assert_equal("yay", res[:one])
- assert_equal("rah", res[:three])
-
- assert_equal({:one => "yay", :three => "rah"}, res.to_hash)
+ ref = res.instance_variable_get("@ref")
+ assert_equal("resource", ref.type, "did not set resource type")
+ assert_equal("testing", ref.title, "did not set resource title")
end
- def test_override
+ def test_merge
res = mkresource
+ other = mkresource
- # Now verify we can't override with any random class
- assert_raise(Puppet::ParseError) do
- res.set paramify(@scope.findclass("other"), "one" => "boo").shift
+ # First try the case where the resource is not allowed to override
+ res.source = "source1"
+ other.source = "source2"
+ other.source.expects(:child_of?).with("source1").returns(false)
+ assert_raise(Puppet::ParseError, "Allowed unrelated resources to override") do
+ res.merge(other)
end
- # And that we can with a subclass
- assert_nothing_raised do
- res.set paramify(@scope.findclass("sub1"), "one" => "boo").shift
- end
+ # Next try it when the sources are equal.
+ res.source = "source3"
+ other.source = res.source
+ other.source.expects(:child_of?).with("source3").never
+ params = {:a => :b, :c => :d}
+ other.expects(:params).returns(params)
+ res.expects(:override_parameter).with(:b)
+ res.expects(:override_parameter).with(:d)
+ res.merge(other)
+
+ # And then parentage is involved
+ other = mkresource
+ res.source = "source3"
+ other.source = "source4"
+ other.source.expects(:child_of?).with("source3").returns(true)
+ params = {:a => :b, :c => :d}
+ other.expects(:params).returns(params)
+ res.expects(:override_parameter).with(:b)
+ res.expects(:override_parameter).with(:d)
+ res.merge(other)
+ end
- # And that a different subclass can override a different parameter
- assert_nothing_raised do
- res.set paramify(@scope.findclass("sub2"), "three" => "boo").shift
- end
+ # the [] method
+ def test_array_accessors
+ res = mkresource
+ params = res.instance_variable_get("@params")
+ assert_nil(res[:missing], "Found a missing parameter somehow")
+ params[:something] = stub(:value => "yay")
+ assert_equal("yay", res[:something], "Did not correctly call value on the parameter")
- # But not the same one
- assert_raise(Puppet::ParseError) do
- res.set paramify(@scope.findclass("sub2"), "one" => "something").shift
- end
+ res.expects(:title).returns(:mytitle)
+ assert_equal(:mytitle, res[:title], "Did not call title when asked for it as a param")
end
- def check_paramadd(val1, val2, merged_val)
- res = mkresource :params => {"one" => val1}
- assert_nothing_raised do
- res.set Parser::Resource::Param.new(
- :name => "one", :value => val2,
- :add => true, :source => @scope.findclass("sub1"))
- end
- assert_equal(merged_val, res[:one])
+ # Make sure any defaults stored in the scope get added to our resource.
+ def test_add_defaults
+ res = mkresource
+ params = res.instance_variable_get("@params")
+ params[:a] = :b
+ res.scope.expects(:lookupdefaults).with(res.type).returns(:a => :replaced, :c => :d)
+ res.expects(:debug)
+
+ res.send(:add_defaults)
+ assert_equal(:d, params[:c], "Did not set default")
+ assert_equal(:b, params[:a], "Replaced parameter with default")
end
- def test_paramadd
- check_paramadd([], [], [])
- check_paramadd([], "rah", ["rah"])
- check_paramadd([], ["rah", "bah"], ["rah", "bah"])
-
- check_paramadd("yay", [], ["yay"])
- check_paramadd("yay", "rah", ["yay", "rah"])
- check_paramadd("yay", ["rah", "bah"], ["yay", "rah", "bah"])
-
- check_paramadd(["yay", "boo"], [], ["yay", "boo"])
- check_paramadd(["yay", "boo"], "rah", ["yay", "boo", "rah"])
- check_paramadd(["yay", "boo"], ["rah", "bah"],
- ["yay", "boo", "rah", "bah"])
+ def test_finish
+ res = mkresource
+ res.expects(:add_overrides)
+ res.expects(:add_defaults)
+ res.expects(:add_metaparams)
+ res.expects(:validate)
+ res.finish
end
- def test_merge
- # Start with the normal one
+ # Make sure we paramcheck our params
+ def test_validate
res = mkresource
+ params = res.instance_variable_get("@params")
+ params[:one] = :two
+ params[:three] = :four
+ res.expects(:paramcheck).with(:one)
+ res.expects(:paramcheck).with(:three)
+ res.send(:validate)
+ end
- # Now create a resource from a different scope
- other = mkresource :source => other, :params => {"one" => "boo"}
-
- # Make sure we can't merge it
- assert_raise(Puppet::ParseError) do
- res.merge(other)
- end
-
- # Make one from a subscope
- other = mkresource :source => "sub1", :params => {"one" => "boo"}
-
- # Make sure it merges
- assert_nothing_raised do
- res.merge(other)
- end
+ def test_override_parameter
+ res = mkresource
+ params = res.instance_variable_get("@params")
+
+ # There are three cases, with the second having two options:
+
+ # No existing parameter.
+ param = stub(:name => "myparam")
+ res.send(:override_parameter, param)
+ assert_equal(param, params["myparam"], "Override was not added to param list")
+
+ # An existing parameter that we can override.
+ source = stub(:child_of? => true)
+ # Start out without addition
+ params["param2"] = stub(:source => :whatever)
+ param = stub(:name => "param2", :source => source, :add => false)
+ res.send(:override_parameter, param)
+ assert_equal(param, params["param2"], "Override was not added to param list")
+
+ # Try with addition.
+ params["param2"] = stub(:value => :a, :source => :whatever)
+ param = stub(:name => "param2", :source => source, :add => true, :value => :b)
+ param.expects(:value=).with([:a, :b])
+ res.send(:override_parameter, param)
+ assert_equal(param, params["param2"], "Override was not added to param list")
+
+ # And finally, make sure we throw an exception when the sources aren't related
+ source = stub(:child_of? => false)
+ params["param2"] = stub(:source => :whatever, :file => :f, :line => :l)
+ old = params["param2"]
+ param = stub(:name => "param2", :source => source, :file => :f, :line => :l)
+ assert_raise(Puppet::ParseError, "Did not fail when params conflicted") do
+ res.send(:override_parameter, param)
+ end
+ assert_equal(old, params["param2"], "Param was replaced irrespective of conflict")
+ end
- assert_equal("boo", res["one"])
+ def test_set_parameter
+ res = mkresource
+ params = res.instance_variable_get("@params")
+
+ # First test the simple case: It's already a parameter
+ param = mock('param')
+ param.expects(:is_a?).with(Resource::Param).returns(true)
+ param.expects(:name).returns("pname")
+ res.send(:set_parameter, param)
+ assert_equal(param, params["pname"], "Parameter was not added to hash")
+
+ # Now the case where there's no value but it's not a param
+ param = mock('param')
+ param.expects(:is_a?).with(Resource::Param).returns(false)
+ assert_raise(ArgumentError, "Did not fail when a non-param was passed") do
+ res.send(:set_parameter, param)
+ end
+
+ # and the case where a value is passed in
+ param = stub :name => "pname", :value => "whatever"
+ Resource::Param.expects(:new).with(:name => "pname", :value => "myvalue", :source => res.source).returns(param)
+ res.send(:set_parameter, "pname", "myvalue")
+ assert_equal(param, params["pname"], "Did not put param in hash")
end
def test_paramcheck
- # First make a builtin resource
- res = nil
- assert_nothing_raised do
- res = Parser::Resource.new :type => "file", :title => tempfile(),
- :source => @source, :scope => @scope
- end
-
- %w{path group source schedule subscribe}.each do |param|
- assert_nothing_raised("Param %s was considered invalid" % param) do
- res.paramcheck(param)
- end
- end
-
- %w{this bad noness}.each do |param|
- assert_raise(Puppet::ParseError, "%s was considered valid" % param) do
- res.paramcheck(param)
- end
- end
-
- # Now create a defined resource
- assert_nothing_raised do
- res = Parser::Resource.new :type => "resource", :title => "yay",
- :source => @source, :scope => @scope
- end
+ # There are three cases here:
- %w{one two three schedule subscribe}.each do |param|
- assert_nothing_raised("Param %s was considered invalid" % param) do
- res.paramcheck(param)
- end
- end
-
- %w{this bad noness}.each do |param|
- assert_raise(Puppet::ParseError, "%s was considered valid" % param) do
- res.paramcheck(param)
- end
- end
+ # It's a valid parameter
+ res = mkresource
+ ref = mock('ref')
+ res.instance_variable_set("@ref", ref)
+ klass = mock("class")
+ ref.expects(:typeclass).returns(klass).times(4)
+ klass.expects(:validattr?).with("good").returns(true)
+ assert(res.send(:paramcheck, :good), "Did not allow valid param")
+
+ # It's name or title
+ klass.expects(:validattr?).with("name").returns(false)
+ assert(res.send(:paramcheck, :name), "Did not allow name")
+ klass.expects(:validattr?).with("title").returns(false)
+ assert(res.send(:paramcheck, :title), "Did not allow title")
+
+ # It's not actually allowed
+ klass.expects(:validattr?).with("other").returns(false)
+ res.expects(:fail)
+ ref.expects(:type)
+ res.send(:paramcheck, :other)
end
- def test_to_trans
+ def test_to_transobject
# First try translating a builtin resource. Make sure we use some references
# and arrays, to make sure they translate correctly.
+ source = mock("source")
+ scope = mkscope
+ scope.stubs(:tags).returns([])
refs = []
4.times { |i| refs << Puppet::Parser::Resource::Reference.new(:title => "file%s" % i, :type => "file") }
res = Parser::Resource.new :type => "file", :title => "/tmp",
- :source => @source, :scope => @scope,
- :params => paramify(@source, :owner => "nobody", :group => %w{you me},
+ :source => source, :scope => scope,
+ :params => paramify(source, :owner => "nobody", :group => %w{you me},
:require => refs[0], :ignore => %w{svn},
:subscribe => [refs[1], refs[2]], :notify => [refs[3]])
@@ -186,168 +244,91 @@ class TestResource < PuppetTest::TestCase
assert_equal(["file", refs[3].title], obj["notify"], "Array with single resource reference was not turned into single value")
end
- def test_adddefaults
- # Set some defaults at the top level
- top = {:one => "fun", :two => "shoe"}
-
- @scope.setdefaults("resource", paramify(@source, top))
-
- # Make a resource at that level
- res = Parser::Resource.new :type => "resource", :title => "yay",
- :source => @source, :scope => @scope
-
- # Add the defaults
- assert_nothing_raised do
- res.adddefaults
- end
-
- # And make sure we got them
- top.each do |p, v|
- assert_equal(v, res[p])
- end
-
- # Now got a bit lower
- other = @scope.newscope
-
- # And create a resource
- lowerres = Parser::Resource.new :type => "resource", :title => "funtest",
- :source => @source, :scope => other
-
- assert_nothing_raised do
- lowerres.adddefaults
- end
-
- # And check
- top.each do |p, v|
- assert_equal(v, lowerres[p])
- end
-
- # Now add some of our own defaults
- lower = {:one => "shun", :three => "free"}
- other.setdefaults("resource", paramify(@source, lower))
- otherres = Parser::Resource.new :type => "resource", :title => "yaytest",
- :source => @source, :scope => other
-
- should = top.dup
- # Make sure the lower defaults beat the higher ones.
- lower.each do |p, v| should[p] = v end
+ # FIXME This isn't a great test, but I need to move on.
+ def test_to_transbucket
+ bucket = mock("transbucket")
+ source = mock("source")
+ scope = mkscope
+ res = Parser::Resource.new :type => "mydefine", :title => "yay",
+ :source => source, :scope => scope
- otherres.adddefaults
- should.each do |p,v|
- assert_equal(v, otherres[p])
- end
+ result = res.to_trans
+ assert_equal("yay", result.name, "did not set bucket name correctly")
+ assert_equal("mydefine", result.type, "did not set bucket type correctly")
end
def test_evaluate
- # Make a definition that we know will, um, do something
- @interp.newdefine "evaltest",
- :arguments => [%w{one}, ["two", stringobj("755")]],
- :code => resourcedef("file", "/tmp",
- "owner" => varref("one"), "mode" => varref("two"))
-
- res = Parser::Resource.new :type => "evaltest", :title => "yay",
- :source => @source, :scope => @scope,
- :params => paramify(@source, :one => "nobody")
-
- # Now try evaluating
- ret = nil
- assert_nothing_raised do
- ret = res.evaluate
- end
-
- # Make sure we can find our object now
- result = @scope.findresource("File[/tmp]")
-
- # Now make sure we got the code we expected.
- assert_instance_of(Puppet::Parser::Resource, result)
-
- assert_equal("file", result.type)
- assert_equal("/tmp", result.title)
- assert_equal("nobody", result["owner"])
- assert_equal("755", result["mode"])
-
- # And that we cannot find the old resource
- assert_nil(@scope.findresource("Evaltest[yay]"),
- "Evaluated resource was not deleted")
+ # First try the most common case, we're not a builtin type.
+ res = mkresource
+ ref = res.instance_variable_get("@ref")
+ type = mock("type")
+ ref.expects(:definedtype).returns(type)
+ res.expects(:finish)
+ res.scope = mock("scope")
+ config = mock("config")
+ res.scope.expects(:compile).returns(config)
+ config.expects(:delete_resource).with(res)
+
+ args = {:scope => res.scope, :resource => res}
+ type.expects(:evaluate).with(args)
+
+ res.evaluate
end
- def test_addoverrides
- # First create an override for an object that doesn't yet exist
- over1 = mkresource :source => "sub1", :params => {:one => "yay"}
-
- assert_nothing_raised do
- @scope.setoverride(over1)
- end
-
- assert(over1.override, "Override was not marked so")
-
- # Now make the resource
- res = mkresource :source => "base", :params => {:one => "rah",
- :three => "foo"}
-
- # And add it to our scope
- @scope.setresource(res)
-
- # And make sure over1 has not yet taken affect
- assert_equal("foo", res[:three], "Lost value")
-
- # Now add an immediately binding override
- over2 = mkresource :source => "sub1", :params => {:three => "yay"}
-
- assert_nothing_raised do
- @scope.setoverride(over2)
- end
-
- # And make sure it worked
- assert_equal("yay", res[:three], "Override 2 was ignored")
-
- # Now add our late-binding override
- assert_nothing_raised do
- res.addoverrides
- end
-
- # And make sure they're still around
- assert_equal("yay", res[:one], "Override 1 lost")
- assert_equal("yay", res[:three], "Override 2 lost")
-
- # And finally, make sure that there are no remaining overrides
- assert_nothing_raised do
- res.addoverrides
- end
+ def test_add_overrides
+ # Try it with nil
+ res = mkresource
+ res.scope = mock('scope')
+ config = mock("config")
+ res.scope.expects(:compile).returns(config)
+ config.expects(:resource_overrides).with(res).returns(nil)
+ res.expects(:merge).never
+ res.send(:add_overrides)
+
+ # And an empty array
+ res = mkresource
+ res.scope = mock('scope')
+ config = mock("config")
+ res.scope.expects(:compile).returns(config)
+ config.expects(:resource_overrides).with(res).returns([])
+ res.expects(:merge).never
+ res.send(:add_overrides)
+
+ # And with some overrides
+ res = mkresource
+ res.scope = mock('scope')
+ config = mock("config")
+ res.scope.expects(:compile).returns(config)
+ returns = %w{a b}
+ config.expects(:resource_overrides).with(res).returns(returns)
+ res.expects(:merge).with("a")
+ res.expects(:merge).with("b")
+ res.send(:add_overrides)
+ assert(returns.empty?, "Did not clear overrides")
end
def test_proxymethods
res = Parser::Resource.new :type => "evaltest", :title => "yay",
- :source => @source, :scope => @scope
+ :source => mock("source"), :scope => mkscope
assert_equal("evaltest", res.type)
assert_equal("yay", res.title)
assert_equal(false, res.builtin?)
end
- def test_addmetaparams
- mkevaltest @interp
- res = Parser::Resource.new :type => "evaltest", :title => "yay",
- :source => @source, :scope => @scope,
- :params => paramify(@source, :tag => "yay")
-
- assert_nil(res[:schedule], "Got schedule already")
- assert_nothing_raised do
- res.addmetaparams
- end
- @scope.setvar("schedule", "daily")
-
- # This is so we can test that it won't override already-set metaparams
- @scope.setvar("tag", "funtest")
+ def test_add_metaparams
+ res = mkresource
+ params = res.instance_variable_get("@params")
+ params[:a] = :b
+ Puppet::Type.expects(:eachmetaparam).multiple_yields(:a, :b, :c)
+ res.scope.expects(:lookupvar).with("b", false).returns(:something)
+ res.scope.expects(:lookupvar).with("c", false).returns(:undefined)
+ res.expects(:set_parameter).with(:b, :something)
- assert_nothing_raised do
- res.addmetaparams
- end
+ res.send(:add_metaparams)
- assert_equal("daily", res[:schedule], "Did not get metaparam")
- assert_equal("yay", res[:tag], "Overrode explicitly-set metaparam")
- assert_nil(res[:noop], "Got invalid metaparam")
+ assert_nil(params[:c], "A value was created somehow for an unset metaparam")
end
def test_reference_conversion
@@ -357,6 +338,7 @@ class TestResource < PuppetTest::TestCase
# Now create an obj that uses it
res = mkresource :type => "file", :title => "/tmp/resource",
:params => {:require => ref}
+ res.scope = mkscope
trans = nil
assert_nothing_raised do
@@ -370,6 +352,7 @@ class TestResource < PuppetTest::TestCase
two = Parser::Resource::Reference.new(:type => "file", :title => "/tmp/ref2")
res = mkresource :type => "file", :title => "/tmp/resource2",
:params => {:require => [ref, two]}
+ res.scope = mkscope
trans = nil
assert_nothing_raised do
@@ -391,63 +374,27 @@ class TestResource < PuppetTest::TestCase
def test_components_are_not_builtin
ref = Parser::Resource::Reference.new(:type => "component", :title => "yay")
- assert_nil(ref.builtintype, "Component was considered builtin")
- end
-
- # #472. Really, this still isn't the best behaviour, but at least
- # it's consistent with what we have elsewhere.
- def test_defaults_from_parent_classes
- # Make a parent class with some defaults in it
- @interp.newclass("base",
- :code => defaultobj("file", :owner => "root", :group => "root")
- )
-
- # Now a mid-level class with some different values
- @interp.newclass("middle", :parent => "base",
- :code => defaultobj("file", :owner => "bin", :mode => "755")
- )
-
- # Now a lower class with its own defaults plus a resource
- @interp.newclass("bottom", :parent => "middle",
- :code => AST::ASTArray.new(:children => [
- defaultobj("file", :owner => "adm", :recurse => "true"),
- resourcedef("file", "/tmp/yayness", {})
- ])
- )
-
- # Now evaluate the class.
- assert_nothing_raised("Failed to evaluate class tree") do
- @scope.evalclasses("bottom")
- end
-
- # Make sure our resource got created.
- res = @scope.findresource("File[/tmp/yayness]")
- assert_nothing_raised("Could not add defaults") do
- res.adddefaults
- end
- assert(res, "could not find resource")
- {:owner => "adm", :recurse => "true", :group => "root", :mode => "755"}.each do |param, value|
- assert_equal(value, res[param], "%s => %s did not inherit correctly" %
- [param, value])
- end
+ assert_nil(ref.builtintype, "Definition was considered builtin")
end
# The second part of #539 - make sure resources pass the arguments
# correctly.
def test_title_with_definitions
- define = @interp.newdefine "yayness",
+ parser = mkparser
+ define = parser.newdefine "yayness",
:code => resourcedef("file", "/tmp",
"owner" => varref("name"), "mode" => varref("title"))
- klass = @interp.findclass("", "")
+
+ klass = parser.findclass("", "")
should = {:name => :owner, :title => :mode}
[
{:name => "one", :title => "two"},
{:title => "three"},
].each do |hash|
- scope = mkscope :interp => @interp
+ config = mkcompile parser
args = {:type => "yayness", :title => hash[:title],
- :source => klass, :scope => scope}
+ :source => klass, :scope => config.topscope}
if hash[:name]
args[:params] = {:name => hash[:name]}
else
@@ -462,7 +409,7 @@ class TestResource < PuppetTest::TestCase
res.evaluate
end
- made = scope.findresource("File[/tmp]")
+ made = config.topscope.findresource("File[/tmp]")
assert(made, "Did not create resource with %s" % hash.inspect)
should.each do |orig, param|
assert_equal(hash[orig] || hash[:title], made[param],
@@ -474,7 +421,7 @@ class TestResource < PuppetTest::TestCase
# part of #629 -- the undef keyword. Make sure 'undef' params get skipped.
def test_undef_and_to_hash
res = mkresource :type => "file", :title => "/tmp/testing",
- :source => @source, :scope => @scope,
+ :source => mock("source"), :scope => mkscope,
:params => {:owner => :undef, :mode => "755"}
hash = nil
@@ -487,11 +434,14 @@ class TestResource < PuppetTest::TestCase
# #643 - Make sure virtual defines result in virtual resources
def test_virtual_defines
- define = @interp.newdefine("yayness",
+ parser = mkparser
+ define = parser.newdefine("yayness",
:code => resourcedef("file", varref("name"),
"mode" => "644"))
- res = mkresource :type => "yayness", :title => "foo", :params => {}
+ config = mkcompile(parser)
+
+ res = mkresource :type => "yayness", :title => "foo", :params => {}, :scope => config.topscope
res.virtual = true
result = nil
@@ -506,7 +456,7 @@ class TestResource < PuppetTest::TestCase
assert(newres.virtual?, "Virtual defined resource generated non-virtual resources")
# Now try it with exported resources
- res = mkresource :type => "yayness", :title => "bar", :params => {}
+ res = mkresource :type => "yayness", :title => "bar", :params => {}, :scope => config.topscope
res.exported = true
result = nil
@@ -521,6 +471,37 @@ class TestResource < PuppetTest::TestCase
assert(newres.exported?, "Exported defined resource generated non-exported resources")
assert(newres.virtual?, "Exported defined resource generated non-virtual resources")
end
-end
-# $Id$
+ # Make sure tags behave appropriately.
+ def test_tags
+ scope_resource = stub 'scope_resource', :tags => %w{srone srtwo}
+ scope = stub 'scope', :resource => scope_resource
+ resource = Puppet::Parser::Resource.new(:type => "file", :title => "yay", :scope => scope, :source => mock('source'))
+
+ # Make sure we get the scope resource's tags, plus the type and title
+ %w{srone srtwo yay file}.each do |tag|
+ assert(resource.tags.include?(tag), "Did not tag resource with %s" % tag)
+ end
+
+ # make sure we can only set legal tags
+ ["an invalid tag", "-anotherinvalid", "bad*tag"].each do |tag|
+ assert_raise(Puppet::ParseError, "Tag #{tag} was considered valid") do
+ resource.tag tag
+ end
+ end
+
+ # make sure good tags make it through.
+ tags = %w{good-tag yaytag GoodTag another_tag a ab A}
+ tags.each do |tag|
+ assert_nothing_raised("Tag #{tag} was considered invalid") do
+ resource.tag tag
+ end
+ end
+
+ # make sure we get each of them.
+ ptags = resource.tags
+ tags.each do |tag|
+ assert(ptags.include?(tag), "missing #{tag}")
+ end
+ end
+end
diff --git a/test/language/scope.rb b/test/language/scope.rb
index f0feee156..74f47b136 100755
--- a/test/language/scope.rb
+++ b/test/language/scope.rb
@@ -27,79 +27,37 @@ class TestScope < Test::Unit::TestCase
end
def test_variables
- scope = nil
- over = "over"
+ config = mkcompile
+ topscope = config.topscope
+ midscope = config.newscope(topscope)
+ botscope = config.newscope(midscope)
- scopes = []
- vars = []
- values = {}
- ovalues = []
+ scopes = {:top => topscope, :mid => midscope, :bot => botscope}
- 10.times { |index|
- # slap some recursion in there
- scope = mkscope(:parent => scope)
- scopes.push scope
-
- var = "var%s" % index
- value = rand(1000)
- ovalue = rand(1000)
-
- ovalues.push ovalue
-
- vars.push var
- values[var] = value
-
- # set the variable in the current scope
- assert_nothing_raised {
- scope.setvar(var,value)
- }
+ # Set a variable in the top and make sure all three can get it
+ topscope.setvar("first", "topval")
+ scopes.each do |name, scope|
+ assert_equal("topval", scope.lookupvar("first", false), "Could not find var in %s" % name)
+ end
- # this should override previous values
- assert_nothing_raised {
- scope.setvar(over,ovalue)
- }
+ # Now set a var in the midscope and make sure the mid and bottom can see it but not the top
+ midscope.setvar("second", "midval")
+ assert_equal(:undefined, scopes[:top].lookupvar("second", false), "Found child var in top scope")
+ [:mid, :bot].each do |name|
+ assert_equal("midval", scopes[name].lookupvar("second", false), "Could not find var in %s" % name)
+ end
- assert_equal(value,scope.lookupvar(var))
-
- #puts "%s vars, %s scopes" % [vars.length,scopes.length]
- i = 0
- vars.zip(scopes) { |v,s|
- # this recurses all the way up the tree as necessary
- val = nil
- oval = nil
-
- # look up the values using the bottom scope
- assert_nothing_raised {
- val = scope.lookupvar(v)
- oval = scope.lookupvar(over)
- }
-
- # verify they're correct
- assert_equal(values[v],val)
- assert_equal(ovalue,oval)
-
- # verify that we get the most recent value
- assert_equal(ovalue,scope.lookupvar(over))
-
- # verify that they aren't available in upper scopes
- if parent = s.parent
- val = nil
- assert_nothing_raised {
- val = parent.lookupvar(v)
- }
- assert_equal("", val, "Did not get empty string on missing var")
-
- # and verify that the parent sees its correct value
- assert_equal(ovalues[i - 1],parent.lookupvar(over))
- end
- i += 1
- }
- }
+ # And set something in the bottom, and make sure we only find it there.
+ botscope.setvar("third", "botval")
+ [:top, :mid].each do |name|
+ assert_equal(:undefined, scopes[name].lookupvar("third", false), "Found child var in top scope")
+ end
+ assert_equal("botval", scopes[:bot].lookupvar("third", false), "Could not find var in bottom scope")
end
def test_lookupvar
- interp = mkinterp
- scope = mkscope :interp => interp
+ parser = mkparser
+ scope = mkscope :parser => parser
# first do the plain lookups
assert_equal("", scope.lookupvar("var"), "scope did not default to string")
@@ -111,7 +69,7 @@ class TestScope < Test::Unit::TestCase
assert_equal("yep", scope.lookupvar("var"), "did not retrieve value correctly")
# Now test the parent lookups
- subscope = mkscope :interp => interp
+ subscope = mkscope :parser => parser
subscope.parent = scope
assert_equal("", subscope.lookupvar("nope"), "scope did not default to string with parent")
assert_equal("", subscope.lookupvar("nope", true), "scope ignored usestring setting with parent")
@@ -129,14 +87,14 @@ class TestScope < Test::Unit::TestCase
end
def test_lookup_qualified_var
- interp = mkinterp
- scope = mkscope :interp => interp
+ parser = mkparser
+ scope = mkscope :parser => parser
scopes = {}
classes = ["", "one", "one::two", "one::two::three"].each do |name|
- klass = interp.newclass(name)
- klass.evaluate(:scope => scope)
- scopes[name] = scope.class_scope(klass)
+ klass = parser.newclass(name)
+ Puppet::Parser::Resource.new(:type => "class", :title => name, :scope => scope, :source => mock('source')).evaluate
+ scopes[name] = scope.compile.class_scope(klass)
end
classes.each do |name|
@@ -149,7 +107,7 @@ class TestScope < Test::Unit::TestCase
def test_declarative
# set to declarative
- top = mkscope(:declarative => true)
+ top = mkscope
sub = mkscope(:parent => top)
assert_nothing_raised {
@@ -166,97 +124,78 @@ class TestScope < Test::Unit::TestCase
}
end
- def test_notdeclarative
- # set to not declarative
- top = mkscope(:declarative => false)
- sub = mkscope(:parent => top)
+ def test_setdefaults
+ config = mkcompile
- assert_nothing_raised {
- top.setvar("test","value")
- }
- assert_nothing_raised {
- top.setvar("test","other")
- }
- assert_nothing_raised {
- sub.setvar("test","later")
- }
- assert_nothing_raised {
- sub.setvar("test","yayness")
- }
- end
+ scope = config.topscope
- def test_setdefaults
- interp, scope, source = mkclassframing
+ defaults = scope.instance_variable_get("@defaults")
- # The setdefaults method doesn't really check what we're doing,
- # so we're just going to use fake defaults here.
+ # First the case where there are no defaults and we pass a single param
+ param = stub :name => "myparam", :file => "f", :line => "l"
+ scope.setdefaults(:mytype, param)
+ assert_equal({"myparam" => param}, defaults[:mytype], "Did not set default correctly")
- # First do a simple local lookup
- params = paramify(source, :one => "fun", :two => "shoe")
- origshould = {}
- params.each do |p| origshould[p.name] = p end
- assert_nothing_raised do
- scope.setdefaults(:file, params)
- end
+ # Now the case where we pass in multiple parameters
+ param1 = stub :name => "one", :file => "f", :line => "l"
+ param2 = stub :name => "two", :file => "f", :line => "l"
+ scope.setdefaults(:newtype, [param1, param2])
+ assert_equal({"one" => param1, "two" => param2}, defaults[:newtype], "Did not set multiple defaults correctly")
- ret = nil
- assert_nothing_raised do
- ret = scope.lookupdefaults(:file)
+ # And the case where there's actually a conflict. Use the first default for this.
+ newparam = stub :name => "myparam", :file => "f", :line => "l"
+ assert_raise(Puppet::ParseError, "Allowed resetting of defaults") do
+ scope.setdefaults(:mytype, param)
end
+ assert_equal({"myparam" => param}, defaults[:mytype], "Replaced default even though there was a failure")
+ end
- assert_equal(origshould, ret)
-
- # Now create a subscope and add some more params.
- newscope = scope.newscope
+ def test_lookupdefaults
+ config = mkcompile
+ top = config.topscope
- newparams = paramify(source, :one => "shun", :three => "free")
- assert_nothing_raised {
- newscope.setdefaults(:file, newparams)
- }
+ # Make a subscope
+ sub = config.newscope(top)
- # And make sure we get the appropriate ones back
- should = {}
- params.each do |p| should[p.name] = p end
- newparams.each do |p| should[p.name] = p end
+ topdefs = top.instance_variable_get("@defaults")
+ subdefs = sub.instance_variable_get("@defaults")
- assert_nothing_raised do
- ret = newscope.lookupdefaults(:file)
- end
+ # First add some defaults to our top scope
+ topdefs[:t1] = {:p1 => :p2, :p3 => :p4}
+ topdefs[:t2] = {:p5 => :p6}
- assert_equal(should, ret)
+ # Then the sub scope
+ subdefs[:t1] = {:p1 => :p7, :p8 => :p9}
+ subdefs[:t2] = {:p5 => :p10, :p11 => :p12}
- # Make sure we still only get the originals from the top scope
- assert_nothing_raised do
- ret = scope.lookupdefaults(:file)
+ # Now make sure we get the correct list back
+ result = nil
+ assert_nothing_raised("Could not get defaults") do
+ result = sub.lookupdefaults(:t1)
end
+ assert_equal(:p9, result[:p8], "Did not get child defaults")
+ assert_equal(:p4, result[:p3], "Did not override parent defaults with child default")
+ assert_equal(:p7, result[:p1], "Did not get parent defaults")
+ end
- assert_equal(origshould, ret)
+ def test_parent
+ config = mkcompile
+ top = config.topscope
- # Now create another scope and make sure we only get the top defaults
- otherscope = scope.newscope
- assert_equal(origshould, otherscope.lookupdefaults(:file))
+ # Make a subscope
+ sub = config.newscope(top)
- # And make sure none of the scopes has defaults for other types
- [scope, newscope, otherscope].each do |sc|
- assert_equal({}, sc.lookupdefaults(:exec))
- end
+ assert_equal(top, sub.parent, "Did not find parent scope correctly")
+ assert_equal(top, sub.parent, "Did not find parent scope on second call")
end
def test_strinterp
# Make and evaluate our classes so the qualified lookups work
- interp = mkinterp
- klass = interp.newclass("")
- scope = mkscope(:interp => interp)
- klass.evaluate(:scope => scope)
-
- klass = interp.newclass("one")
- klass.evaluate(:scope => scope)
-
- klass = interp.newclass("one::two")
- klass.evaluate(:scope => scope)
-
+ parser = mkparser
+ klass = parser.newclass("")
+ scope = mkscope(:parser => parser)
+ Puppet::Parser::Resource.new(:type => "class", :title => :main, :scope => scope, :source => mock('source')).evaluate
- scope = scope.class_scope("")
assert_nothing_raised {
scope.setvar("test","value")
}
@@ -264,9 +203,9 @@ class TestScope < Test::Unit::TestCase
scopes = {"" => scope}
%w{one one::two one::two::three}.each do |name|
- klass = interp.newclass(name)
- klass.evaluate(:scope => scope)
- scopes[name] = scope.class_scope(klass)
+ klass = parser.newclass(name)
+ Puppet::Parser::Resource.new(:type => "class", :title => name, :scope => scope, :source => mock('source')).evaluate
+ scopes[name] = scope.compile.class_scope(klass)
scopes[name].setvar("test", "value-%s" % name.sub(/.+::/,''))
end
@@ -298,7 +237,7 @@ class TestScope < Test::Unit::TestCase
tests.each do |input, output|
assert_nothing_raised("Failed to scan %s" % input.inspect) do
assert_equal(output, scope.strinterp(input),
- 'did not interpret %s correctly' % input.inspect)
+ 'did not parserret %s correctly' % input.inspect)
end
end
@@ -311,7 +250,7 @@ class TestScope < Test::Unit::TestCase
string = "\\" + l
assert_nothing_raised do
assert_equal(string, scope.strinterp(string),
- 'did not interpret %s correctly' % string)
+ 'did not parserret %s correctly' % string)
end
assert(logs.detect { |m| m.message =~ /Unrecognised escape/ },
@@ -320,83 +259,21 @@ class TestScope < Test::Unit::TestCase
end
end
- def test_setclass
- interp, scope, source = mkclassframing
-
- base = scope.findclass("base")
- assert(base, "Could not find base class")
- assert(! scope.class_scope(base), "Class incorrectly set")
- assert(! scope.classlist.include?("base"), "Class incorrectly in classlist")
- assert_nothing_raised do
- scope.setclass base
- end
-
- assert(scope.class_scope(base), "Class incorrectly unset")
- assert(scope.classlist.include?("base"), "Class not in classlist")
-
- # Make sure we can retrieve the scope.
- assert_equal(scope, scope.class_scope(base),
- "class scope was not set correctly")
-
- # Now try it with a normal string
- Puppet[:trace] = false
- assert_raise(Puppet::DevError) do
- scope.setclass "string"
- end
-
- assert(! scope.class_scope("string"), "string incorrectly set")
-
- # Set "" in the class list, and make sure it doesn't show up in the return
- top = scope.findclass("")
- assert(top, "Could not find top class")
- scope.setclass top
-
- assert(! scope.classlist.include?(""), "Class list included empty")
- end
-
- def test_validtags
- scope = mkscope()
-
- ["a class", "a.class"].each do |bad|
- assert_raise(Puppet::ParseError, "Incorrectly allowed %s" % bad.inspect) do
- scope.tag(bad)
- end
- end
-
- ["a-class", "a_class", "Class", "class", "yayNess"].each do |good|
- assert_nothing_raised("Incorrectly banned %s" % good.inspect) do
- scope.tag(good)
- end
- end
-
- end
-
def test_tagfunction
- scope = mkscope()
-
- assert_nothing_raised {
- scope.function_tag(["yayness", "booness"])
- }
-
- assert(scope.tags.include?("yayness"), "tag 'yayness' did not get set")
- assert(scope.tags.include?("booness"), "tag 'booness' did not get set")
-
- # Now verify that the 'tagged' function works correctly
- assert(scope.function_tagged("yayness"),
- "tagged function incorrectly returned false")
- assert(scope.function_tagged("booness"),
- "tagged function incorrectly returned false")
+ scope = mkscope
+ resource = mock 'resource'
+ scope.resource = resource
+ resource.expects(:tag).with("yayness", "booness")
- assert(! scope.function_tagged("funtest"),
- "tagged function incorrectly returned true")
+ scope.function_tag(%w{yayness booness})
end
def test_includefunction
- interp = mkinterp
- scope = mkscope :interp => interp
+ parser = mkparser
+ scope = mkscope :parser => parser
- myclass = interp.newclass "myclass"
- otherclass = interp.newclass "otherclass"
+ myclass = parser.newclass "myclass"
+ otherclass = parser.newclass "otherclass"
function = Puppet::Parser::AST::Function.new(
:name => "include",
@@ -410,19 +287,21 @@ class TestScope < Test::Unit::TestCase
function.evaluate :scope => scope
end
+ scope.compile.send(:evaluate_generators)
+
[myclass, otherclass].each do |klass|
- assert(scope.class_scope(klass),
+ assert(scope.compile.class_scope(klass),
"%s was not set" % klass.classname)
end
end
def test_definedfunction
- interp = mkinterp
+ parser = mkparser
%w{one two}.each do |name|
- interp.newdefine name
+ parser.newdefine name
end
- scope = mkscope :interp => interp
+ scope = mkscope :parser => parser
assert_nothing_raised {
%w{one two file user}.each do |type|
@@ -449,62 +328,19 @@ class TestScope < Test::Unit::TestCase
"undef considered true")
end
- # Verify scope context is handled correctly.
- def test_scopeinside
- scope = mkscope()
-
- one = :one
- two = :two
-
- # First just test the basic functionality.
- assert_nothing_raised {
- scope.inside :one do
- assert_equal(:one, scope.inside, "Context did not get set")
- end
- assert_nil(scope.inside, "Context did not revert")
- }
-
- # Now make sure error settings work.
- assert_raise(RuntimeError) {
- scope.inside :one do
- raise RuntimeError, "This is a failure, yo"
- end
- }
- assert_nil(scope.inside, "Context did not revert")
-
- # Now test it a bit deeper in.
- assert_nothing_raised {
- scope.inside :one do
- scope.inside :two do
- assert_equal(:two, scope.inside, "Context did not get set")
- end
- assert_equal(:one, scope.inside, "Context did not get set")
- end
- assert_nil(scope.inside, "Context did not revert")
- }
-
- # And lastly, check errors deeper in
- assert_nothing_raised {
- scope.inside :one do
- begin
- scope.inside :two do
- raise "a failure"
- end
- rescue
- end
- assert_equal(:one, scope.inside, "Context did not get set")
- end
- assert_nil(scope.inside, "Context did not revert")
- }
-
- end
-
if defined? ActiveRecord
# Verify that we recursively mark as exported the results of collectable
# components.
def test_exportedcomponents
- interp, scope, source = mkclassframing
- children = []
+ config = mkcompile
+ parser = config.parser
+
+ # Create a default source
+ config.topscope.source = parser.newclass "", ""
+
+ # And a scope resource
+ scope_res = stub 'scope_resource', :virtual? => true, :exported? => false, :tags => []
+ config.topscope.resource = scope_res
args = AST::ASTArray.new(
:file => tempfile(),
@@ -513,7 +349,7 @@ class TestScope < Test::Unit::TestCase
)
# Create a top-level component
- interp.newdefine "one", :arguments => [%w{arg}],
+ parser.newdefine "one", :arguments => [%w{arg}],
:code => AST::ASTArray.new(
:children => [
resourcedef("file", "/tmp", {"owner" => varref("arg")})
@@ -521,7 +357,7 @@ class TestScope < Test::Unit::TestCase
)
# And a component that calls it
- interp.newdefine "two", :arguments => [%w{arg}],
+ parser.newdefine "two", :arguments => [%w{arg}],
:code => AST::ASTArray.new(
:children => [
resourcedef("one", "ptest", {"arg" => varref("arg")})
@@ -529,7 +365,7 @@ class TestScope < Test::Unit::TestCase
)
# And then a third component that calls the second
- interp.newdefine "three", :arguments => [%w{arg}],
+ parser.newdefine "three", :arguments => [%w{arg}],
:code => AST::ASTArray.new(
:children => [
resourcedef("two", "yay", {"arg" => varref("arg")})
@@ -542,13 +378,14 @@ class TestScope < Test::Unit::TestCase
# And mark it as exported
obj.exported = true
- obj.evaluate :scope => scope
-
# And then evaluate it
- interp.evaliterate(scope)
+ obj.evaluate :scope => config.topscope
+
+ # And run the loop.
+ config.send(:evaluate_generators)
%w{file}.each do |type|
- objects = scope.lookupexported(type)
+ objects = config.resources.find_all { |r| r.type == type and r.exported }
assert(!objects.empty?, "Did not get an exported %s" % type)
end
@@ -582,15 +419,17 @@ Host <<||>>"
)
}
- objects = nil
+ config = nil
# We run it twice because we want to make sure there's no conflict
# if we pull it up from the database.
+ node = mknode
+ node.parameters = {"hostname" => node.name}
2.times { |i|
assert_nothing_raised {
- objects = interp.run("localhost", {"hostname" => "localhost"})
+ config = interp.compile(node)
}
- flat = objects.flatten
+ flat = config.extract.flatten
%w{puppet myhost}.each do |name|
assert(flat.find{|o| o.name == name }, "Did not find #{name}")
@@ -601,94 +440,8 @@ Host <<||>>"
$stderr.puts "No ActiveRecord -- skipping collection tests"
end
- # Make sure tags behave appropriately.
- def test_tags
- interp, scope, source = mkclassframing
-
- # First make sure we can only set legal tags
- ["an invalid tag", "-anotherinvalid", "bad*tag"].each do |tag|
- assert_raise(Puppet::ParseError, "Tag #{tag} was considered valid") do
- scope.tag tag
- end
- end
-
- # Now make sure good tags make it through.
- tags = %w{good-tag yaytag GoodTag another_tag a ab A}
- tags.each do |tag|
- assert_nothing_raised("Tag #{tag} was considered invalid") do
- scope.tag tag
- end
- end
-
- # And make sure we get each of them.
- ptags = scope.tags
- tags.each do |tag|
- assert(ptags.include?(tag), "missing #{tag}")
- end
-
-
- # Now create a subscope and set some tags there
- newscope = scope.newscope(:type => 'subscope')
-
- # set some tags
- newscope.tag "onemore", "yaytag"
-
- # And make sure we get them plus our parent tags
- assert_equal((ptags + %w{onemore subscope}).sort, newscope.tags.sort)
- end
-
- # Make sure we successfully translate objects
- def test_translate
- interp, scope, source = mkclassframing
-
- # Create a define that we'll be using
- interp.newdefine("wrapper", :code => AST::ASTArray.new(:children => [
- resourcedef("file", varref("name"), "owner" => "root")
- ]))
-
- # Now create a resource that uses that define
- define = mkresource(:type => "wrapper", :title => "/tmp/testing",
- :scope => scope, :source => source, :params => :none)
-
- scope.setresource define
-
- # And a normal resource
- scope.setresource mkresource(:type => "file", :title => "/tmp/rahness",
- :scope => scope, :source => source,
- :params => {:owner => "root"})
-
- # Evaluate the the define thing.
- define.evaluate
-
- # Now the scope should have a resource and a subscope. Translate the
- # whole thing.
- ret = nil
- assert_nothing_raised do
- ret = scope.translate
- end
-
- assert_instance_of(Puppet::TransBucket, ret)
-
- ret.each do |obj|
- assert(obj.is_a?(Puppet::TransBucket) || obj.is_a?(Puppet::TransObject),
- "Got a non-transportable object %s" % obj.class)
- end
-
- rahness = ret.find { |c| c.type == "file" and c.name == "/tmp/rahness" }
- assert(rahness, "Could not find top-level file")
- assert_equal("root", rahness["owner"])
-
- bucket = ret.find { |c| c.class == Puppet::TransBucket and c.name == "/tmp/testing" }
- assert(bucket, "Could not find define bucket")
-
- testing = bucket.find { |c| c.type == "file" and c.name == "/tmp/testing" }
- assert(testing, "Could not find define file")
- assert_equal("root", testing["owner"])
-
- end
-
def test_namespaces
- interp, scope, source = mkclassframing
+ scope = mkscope
assert_equal([""], scope.namespaces,
"Started out with incorrect namespaces")
@@ -701,17 +454,17 @@ Host <<||>>"
end
def test_findclass_and_finddefine
- interp = mkinterp
+ parser = mkparser
- # Make sure our scope calls the interp findclass method with
+ # Make sure our scope calls the parser findclass method with
# the right namespaces
- scope = mkscope :interp => interp
+ scope = mkscope :parser => parser
- interp.metaclass.send(:attr_accessor, :last)
+ parser.metaclass.send(:attr_accessor, :last)
methods = [:findclass, :finddefine]
methods.each do |m|
- interp.meta_def(m) do |namespace, name|
+ parser.meta_def(m) do |namespace, name|
@checked ||= []
@checked << [namespace, name]
@@ -727,7 +480,7 @@ Host <<||>>"
end
test = proc do |should|
- interp.last = scope.namespaces[-1]
+ parser.last = scope.namespaces[-1]
methods.each do |method|
result = scope.send(method, "testing")
assert_equal(should, result,
@@ -760,26 +513,6 @@ Host <<||>>"
assert_equal("", scope.lookupvar("testing", true),
"undef was not returned as '' when string")
end
-
- # #620 - Nodes and classes should conflict, else classes don't get evaluated
- def test_nodes_and_classes_name_conflict
- scope = mkscope
-
- node = AST::Node.new :classname => "test", :namespace => ""
- scope.setclass(node)
-
- assert(scope.nodescope?, "Scope was not marked a node scope when a node was set")
-
- # Now make a subscope that will be a class scope
- klass = AST::HostClass.new :classname => "test", :namespace => ""
- kscope = klass.subscope(scope)
-
- # Now make sure we throw a failure, because we're trying to do a class and node
- # with the same name
- assert_raise(Puppet::ParseError, "Did not fail on class and node with same name") do
- kscope.class_scope(klass)
- end
- end
end
# $Id$
diff --git a/test/language/snippets.rb b/test/language/snippets.rb
index 5fb11e8cd..2c74543e7 100755
--- a/test/language/snippets.rb
+++ b/test/language/snippets.rb
@@ -11,7 +11,6 @@ require 'puppettest'
class TestSnippets < Test::Unit::TestCase
include PuppetTest
- include ObjectSpace
def setup
super
@@ -458,15 +457,28 @@ class TestSnippets < Test::Unit::TestCase
#eval("alias %s %s" % [testname, mname])
testname = ("test_" + mname).intern
self.send(:define_method, testname) {
+ facts = {
+ "hostname" => "testhost",
+ "domain" => "domain.com",
+ "ipaddress" => "127.0.0.1",
+ "fqdn" => "testhost.domain.com"
+ }
+ Facter.stubs(:each)
+ facts.each do |name, value|
+ Facter.stubs(:value).with(name).returns(value)
+ end
# first parse the file
server = Puppet::Network::Handler.master.new(
:Manifest => snippet(file),
:Local => true
)
+ server.send(:fact_handler).stubs(:set)
+ server.send(:fact_handler).stubs(:get).returns(facts)
client = Puppet::Network::Client.master.new(
:Master => server,
:Cache => false
)
+ client.class.stubs(:facts).returns(facts)
assert(client.local)
assert_nothing_raised {
@@ -484,9 +496,6 @@ class TestSnippets < Test::Unit::TestCase
assert_nothing_raised {
client.getconfig()
}
- #assert_nothing_raised {
- # trans = client.apply()
- #}
Puppet::Type.eachtype { |type|
type.each { |obj|
@@ -508,5 +517,3 @@ class TestSnippets < Test::Unit::TestCase
end
}
end
-
-# $Id$
diff --git a/test/lib/puppettest.rb b/test/lib/puppettest.rb
index 8b85966f5..b56bc563e 100755
--- a/test/lib/puppettest.rb
+++ b/test/lib/puppettest.rb
@@ -4,6 +4,7 @@ $LOAD_PATH.unshift(File.expand_path(File.dirname(__FILE__)))
$LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '../../lib')))
require 'puppet'
+require 'mocha'
require 'test/unit'
# Yay; hackish but it works
@@ -282,6 +283,7 @@ module PuppetTest
rescue Timeout::Error
# just move on
end
+ mocha_verify
if File.stat("/dev/null").mode & 007777 != 0666
File.open("/tmp/nullfailure", "w") { |f|
f.puts self.class
diff --git a/test/lib/puppettest/parsertesting.rb b/test/lib/puppettest/parsertesting.rb
index d66367ada..eef0cd8bc 100644
--- a/test/lib/puppettest/parsertesting.rb
+++ b/test/lib/puppettest/parsertesting.rb
@@ -5,6 +5,8 @@ module PuppetTest::ParserTesting
include PuppetTest
AST = Puppet::Parser::AST
+ Compile = Puppet::Parser::Compile
+
# A fake class that we can use for testing evaluation.
class FakeAST
attr_writer :evaluate
@@ -39,6 +41,20 @@ module PuppetTest::ParserTesting
)
end
+ def mkcompile(parser = nil)
+ require 'puppet/network/handler/node'
+ parser ||= mkparser
+ node = mknode
+ return Compile.new(node, parser)
+ end
+
+ def mknode(name = nil)
+ require 'puppet/node'
+ name ||= "nodename"
+ Puppet::Network::Handler.handler(:node)
+ Puppet::Node.new(name)
+ end
+
def mkinterp(args = {})
args[:Code] ||= "" unless args.include?(:Manifest)
args[:Local] ||= true
@@ -50,14 +66,16 @@ module PuppetTest::ParserTesting
end
def mkscope(hash = {})
- hash[:interp] ||= mkinterp
- hash[:source] ||= (hash[:interp].findclass("", "") ||
- hash[:interp].newclass(""))
+ hash[:parser] ||= mkparser
+ compile ||= mkcompile(hash[:parser])
+ compile.topscope.source = (hash[:parser].findclass("", "") || hash[:parser].newclass(""))
- unless hash[:source]
+ unless compile.topscope.source
raise "Could not find source for scope"
end
- Puppet::Parser::Scope.new(hash)
+ # Make the 'main' stuff
+ compile.send(:evaluate_main)
+ compile.topscope
end
def classobj(name, hash = {})
@@ -87,7 +105,7 @@ module PuppetTest::ParserTesting
title = stringobj(title)
end
assert_nothing_raised("Could not create %s %s" % [type, title]) {
- return AST::ResourceDef.new(
+ return AST::Resource.new(
:file => __FILE__,
:line => __LINE__,
:title => title,
@@ -117,7 +135,7 @@ module PuppetTest::ParserTesting
def resourceref(type, title)
assert_nothing_raised("Could not create %s %s" % [type, title]) {
- return AST::ResourceRef.new(
+ return AST::ResourceReference.new(
:file => __FILE__,
:line => __LINE__,
:type => type,
@@ -173,7 +191,7 @@ module PuppetTest::ParserTesting
params = hash.collect { |param, value|
resourceparam(param, value)
}
- return AST::ResourceInst.new(
+ return AST::ResourceInstance.new(
:file => tempfile(),
:line => rand(100),
:children => params
@@ -293,12 +311,12 @@ module PuppetTest::ParserTesting
config = nil
assert_nothing_raised {
- config = interp.run(Facter["hostname"].value, {})
+ config = interp.compile(mknode)
}
comp = nil
assert_nothing_raised {
- comp = config.to_type
+ comp = config.extract.to_type
}
assert_apply(comp)
@@ -388,5 +406,3 @@ module PuppetTest::ParserTesting
return trans
end
end
-
-# $Id$
diff --git a/test/lib/puppettest/railstesting.rb b/test/lib/puppettest/railstesting.rb
index 8b5f074a2..403bdb756 100644
--- a/test/lib/puppettest/railstesting.rb
+++ b/test/lib/puppettest/railstesting.rb
@@ -40,12 +40,10 @@ module PuppetTest::RailsTesting
# Now try storing our crap
host = nil
+ node = mknode(facts["hostname"])
+ node.parameters = facts
assert_nothing_raised {
- host = Puppet::Rails::Host.store(
- :resources => resources,
- :facts => facts,
- :name => facts["hostname"]
- )
+ host = Puppet::Rails::Host.store(node, resources)
}
# Now save the whole thing
diff --git a/test/lib/puppettest/resourcetesting.rb b/test/lib/puppettest/resourcetesting.rb
index 8cb59b83d..325602c22 100644
--- a/test/lib/puppettest/resourcetesting.rb
+++ b/test/lib/puppettest/resourcetesting.rb
@@ -1,25 +1,10 @@
module PuppetTest::ResourceTesting
Parser = Puppet::Parser
AST = Puppet::Parser::AST
- def mkclassframing(interp = nil)
- interp ||= mkinterp
- interp.newdefine("resource", :arguments => [%w{one}, %w{two value}, %w{three}])
- interp.newclass("")
- source = interp.newclass("base")
- interp.newclass("sub1", :parent => "base")
- interp.newclass("sub2", :parent => "base")
- interp.newclass("other")
-
- scope = Parser::Scope.new(:interp => interp)
- scope.source = source
-
- return interp, scope, source
- end
-
- def mkevaltest(interp = nil)
- interp ||= mkinterp
- @interp.newdefine("evaltest",
+ def mkevaltest(parser = nil)
+ parser ||= mkparser
+ @parser.newdefine("evaltest",
:arguments => [%w{one}, ["two", stringobj("755")]],
:code => resourcedef("file", "/tmp",
"owner" => varref("one"), "mode" => varref("two"))
@@ -27,26 +12,14 @@ module PuppetTest::ResourceTesting
end
def mkresource(args = {})
-
- if args[:scope] and ! args[:source]
- args[:source] = args[:scope].source
- end
-
- unless args[:scope]
- unless defined? @scope
- raise "Must set @scope to mkresource"
- end
- end
+ args[:source] ||= "source"
+ args[:scope] ||= mkscope
{:type => "resource", :title => "testing",
- :source => @source, :scope => @scope}.each do |param, value|
+ :source => "source", :scope => "scope"}.each do |param, value|
args[param] ||= value
end
- unless args[:source].is_a?(Puppet::Parser::AST::HostClass)
- args[:source] = args[:scope].findclass(args[:source])
- end
-
params = args[:params] || {:one => "yay", :three => "rah"}
if args[:params] == :none
args.delete(:params)
diff --git a/test/lib/spec/callback.rb b/test/lib/spec/callback.rb
deleted file mode 100644
index aa7ecec0f..000000000
--- a/test/lib/spec/callback.rb
+++ /dev/null
@@ -1,11 +0,0 @@
-require 'spec/callback/callback_container'
-require 'spec/callback/extensions/module'
-require 'spec/callback/extensions/object'
-
-# Callback is a fork of Brian Takita's "callback library" (see http://callback.rubyforge.org),
-# which Brian graciously contributed to RSpec in order to avoid the dependency.
-#
-# RSpec uses Callback internally to create hooks to Spec::Runner events. If you're interested
-# in a simple, powerful API for generating callback events, check out http://callback.rubyforge.org.
-module Callback
-end
diff --git a/test/lib/spec/callback/callback_container.rb b/test/lib/spec/callback/callback_container.rb
deleted file mode 100644
index 24d4c0ced..000000000
--- a/test/lib/spec/callback/callback_container.rb
+++ /dev/null
@@ -1,60 +0,0 @@
-module Callback
- class CallbackContainer
- def initialize
- @callback_registry = Hash.new do |hash, key|
- hash[key] = Array.new
- end
- end
-
- # Defines the callback with the key in this container.
- def define(key, callback_proc=nil, &callback_block)
- callback = extract_callback(callback_block, callback_proc) do
- raise "You must define the callback that accepts the call method."
- end
- @callback_registry[key] << callback
- callback
- end
-
- # Undefines the callback with the key in this container.
- def undefine(key, callback_proc)
- callback = extract_callback(callback_proc) do
- raise "You may only undefine callbacks that use the call method."
- end
- @callback_registry[key].delete callback
- callback
- end
-
- # Notifies the callbacks for the key. Arguments may be passed.
- # An error handler may be passed in as a block. If there is an error, the block is called with
- # error object as an argument.
- # An array of the return values of the callbacks is returned.
- def notify(key, *args, &error_handler)
- @callback_registry[key].collect do |callback|
- begin
- callback.call(*args)
- rescue Exception => e
- yield(e) if error_handler
- end
- end
- end
-
- # Clears all of the callbacks in this container.
- def clear
- @callback_registry.clear
- end
-
- protected
- def extract_callback(first_choice_callback, second_choice_callback = nil)
- callback = nil
- if first_choice_callback
- callback = first_choice_callback
- elsif second_choice_callback
- callback = second_choice_callback
- end
- unless callback.respond_to? :call
- yield
- end
- return callback
- end
- end
-end
diff --git a/test/lib/spec/callback/extensions/module.rb b/test/lib/spec/callback/extensions/module.rb
deleted file mode 100644
index 429268ed1..000000000
--- a/test/lib/spec/callback/extensions/module.rb
+++ /dev/null
@@ -1,24 +0,0 @@
-module Callback
- module ModuleMethods
- # For each event_name submitted, defines a callback event with this name.
- # Client code can then register as a callback listener using object.event_name.
- def callback_events(*event_names)
- event_names.each do |event_name|
- define_callback_event(event_name)
- end
- end
-
- private
- def define_callback_event(event_name)
- module_eval <<-EOS
- def #{event_name}(&block)
- register_callback(:#{event_name}, &block)
- end
- EOS
- end
- end
-end
-
-class Module
- include Callback::ModuleMethods
-end
diff --git a/test/lib/spec/callback/extensions/object.rb b/test/lib/spec/callback/extensions/object.rb
deleted file mode 100644
index c6ac6fd14..000000000
--- a/test/lib/spec/callback/extensions/object.rb
+++ /dev/null
@@ -1,37 +0,0 @@
-module Callback
- module InstanceMethods
- # Registers a callback for the event on the object. The callback can either be a block or a proc.
- # When the callbacks are notified, the return value of the proc is passed to the caller.
- def register_callback(event, callback_proc=nil, &callback_block)
- callbacks.define(event, callback_proc, &callback_block)
- end
-
- # Removes the callback from the event. The callback proc must be the same
- # object as the one that was passed to register_callback.
- def unregister_callback(event, callback_proc)
- callbacks.undefine(event, callback_proc)
- end
-
- protected
- # Notifies the callbacks registered with the event on the object. Arguments can be passed to the callbacks.
- # An error handler may be passed in as a block. If there is an error, the block is called with
- # error object as an argument.
- # An array of the return values of the callbacks is returned.
- def notify_callbacks(event, *args, &error_handler)
- callbacks.notify(event, *args, &error_handler)
- end
-
- def notify_class_callbacks(event, *args, &error_handler)
- self.class.send(:notify_callbacks, event, *args, &error_handler)
- end
-
- # The CallbackContainer for this object.
- def callbacks
- @callbacks ||= CallbackContainer.new
- end
- end
-end
-
-class Object
- include Callback::InstanceMethods
-end \ No newline at end of file
diff --git a/test/lib/spec/deprecated.rb b/test/lib/spec/deprecated.rb
deleted file mode 100644
index e9c1cd829..000000000
--- a/test/lib/spec/deprecated.rb
+++ /dev/null
@@ -1,3 +0,0 @@
-def deprecated(&block)
- block.call unless ENV['RSPEC_DISABLE_DEPRECATED_FEATURES'] == 'true'
-end
diff --git a/test/lib/spec/expectations/extensions/object.rb b/test/lib/spec/expectations/extensions/object.rb
deleted file mode 100644
index dd5498fdd..000000000
--- a/test/lib/spec/expectations/extensions/object.rb
+++ /dev/null
@@ -1,109 +0,0 @@
-module Spec
- module Expectations
- # rspec adds #should and #should_not to every Object (and,
- # implicitly, every Class).
- module ObjectExpectations
-
- # :call-seq:
- # should(matcher)
- # should == expected
- # should =~ expected
- #
- # receiver.should(matcher)
- # => Passes if matcher.matches?(receiver)
- #
- # receiver.should == expected #any value
- # => Passes if (receiver == expected)
- #
- # receiver.should =~ regexp
- # => Passes if (receiver =~ regexp)
- #
- # See Spec::Matchers for more information about matchers
- #
- # == Warning
- #
- # NOTE that this does NOT support receiver.should != expected.
- # Instead, use receiver.should_not == expected
- def should(matcher=nil, &block)
- return ExpectationMatcherHandler.handle_matcher(self, matcher, &block) if matcher
- Should::Should.new(self)
- end
-
- # :call-seq:
- # should_not(matcher)
- # should_not == expected
- # should_not =~ expected
- #
- # receiver.should_not(matcher)
- # => Passes unless matcher.matches?(receiver)
- #
- # receiver.should_not == expected
- # => Passes unless (receiver == expected)
- #
- # receiver.should_not =~ regexp
- # => Passes unless (receiver =~ regexp)
- #
- # See Spec::Matchers for more information about matchers
- def should_not(matcher=nil, &block)
- return NegativeExpectationMatcherHandler.handle_matcher(self, matcher, &block) if matcher
- should.not
- end
-
- deprecated do
- # Deprecated: use should have(n).items (see Spec::Matchers)
- # This will be removed in 0.9
- def should_have(expected)
- should.have(expected)
- end
- alias_method :should_have_exactly, :should_have
-
- # Deprecated: use should have_at_least(n).items (see Spec::Matchers)
- # This will be removed in 0.9
- def should_have_at_least(expected)
- should.have.at_least(expected)
- end
-
- # Deprecated: use should have_at_most(n).items (see Spec::Matchers)
- # This will be removed in 0.9
- def should_have_at_most(expected)
- should.have.at_most(expected)
- end
-
- # Deprecated: use should include(expected) (see Spec::Matchers)
- # This will be removed in 0.9
- def should_include(expected)
- should.include(expected)
- end
-
- # Deprecated: use should_not include(expected) (see Spec::Matchers)
- # This will be removed in 0.9
- def should_not_include(expected)
- should.not.include(expected)
- end
-
- # Deprecated: use should be(expected) (see Spec::Matchers)
- # This will be removed in 0.9
- def should_be(expected = :___no_arg)
- should.be(expected)
- end
-
- # Deprecated: use should_not be(expected) (see Spec::Matchers)
- # This will be removed in 0.9
- def should_not_be(expected = :___no_arg)
- should_not.be(expected)
- end
- end
- end
- end
-end
-
-class Object
- include Spec::Expectations::ObjectExpectations
- deprecated do
- include Spec::Expectations::UnderscoreSugar
- end
-end
-
-deprecated do
- Object.handle_underscores_for_rspec!
-end \ No newline at end of file
diff --git a/test/lib/spec/expectations/extensions/proc.rb b/test/lib/spec/expectations/extensions/proc.rb
deleted file mode 100644
index 8286708ed..000000000
--- a/test/lib/spec/expectations/extensions/proc.rb
+++ /dev/null
@@ -1,57 +0,0 @@
-module Spec
- module Expectations
- module ProcExpectations
- # Given a receiver and a message (Symbol), specifies that the result
- # of sending that message that receiver should change after
- # executing the proc.
- #
- # lambda { @team.add player }.should_change(@team.players, :size)
- # lambda { @team.add player }.should_change(@team.players, :size).by(1)
- # lambda { @team.add player }.should_change(@team.players, :size).to(23)
- # lambda { @team.add player }.should_change(@team.players, :size).from(22).to(23)
- #
- # You can use a block instead of a message and receiver.
- #
- # lambda { @team.add player }.should_change{@team.players.size}
- # lambda { @team.add player }.should_change{@team.players.size}.by(1)
- # lambda { @team.add player }.should_change{@team.players.size}.to(23)
- # lambda { @team.add player }.should_change{@team.players.size}.from(22).to(23)
- def should_change(receiver=nil, message=nil, &block)
- should.change(receiver, message, &block)
- end
-
- # Given a receiver and a message (Symbol), specifies that the result
- # of sending that message that receiver should NOT change after
- # executing the proc.
- #
- # lambda { @team.add player }.should_not_change(@team.players, :size)
- #
- # You can use a block instead of a message and receiver.
- #
- # lambda { @team.add player }.should_not_change{@team.players.size}
- def should_not_change(receiver, message)
- should.not.change(receiver, message)
- end
-
- def should_raise(exception=Exception, message=nil)
- should.raise(exception, message)
- end
-
- def should_not_raise(exception=Exception, message=nil)
- should.not.raise(exception, message)
- end
-
- def should_throw(symbol)
- should.throw(symbol)
- end
-
- def should_not_throw(symbol=:___this_is_a_symbol_that_will_likely_never_occur___)
- should.not.throw(symbol)
- end
- end
- end
-end
-
-class Proc
- include Spec::Expectations::ProcExpectations
-end \ No newline at end of file
diff --git a/test/lib/spec/expectations/handler.rb b/test/lib/spec/expectations/handler.rb
deleted file mode 100644
index 9d3fd1f88..000000000
--- a/test/lib/spec/expectations/handler.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-module Spec
- module Expectations
-
- module MatcherHandlerHelper
- def describe(matcher)
- matcher.respond_to?(:description) ? matcher.description : "[#{matcher.class.name} does not provide a description]"
- end
- end
-
- class ExpectationMatcherHandler
- class << self
- include MatcherHandlerHelper
- def handle_matcher(actual, matcher, &block)
- unless matcher.nil?
- match = matcher.matches?(actual, &block)
- ::Spec::Matchers.generated_description = "should #{describe(matcher)}"
- Spec::Expectations.fail_with(matcher.failure_message) unless match
- end
- end
- end
- end
-
- class NegativeExpectationMatcherHandler
- class << self
- include MatcherHandlerHelper
- def handle_matcher(actual, matcher, &block)
- unless matcher.nil?
- unless matcher.respond_to?(:negative_failure_message)
- Spec::Expectations.fail_with(
- <<-EOF
- Matcher does not support should_not.
- See Spec::Matchers for more information
- about matchers.
- EOF
- )
- end
- match = matcher.matches?(actual, &block)
- ::Spec::Matchers.generated_description = "should not #{describe(matcher)}"
- Spec::Expectations.fail_with(matcher.negative_failure_message) if match
- end
- end
- end
- end
-
- end
-end
-
diff --git a/test/lib/spec/expectations/should.rb b/test/lib/spec/expectations/should.rb
deleted file mode 100644
index f64e6ff78..000000000
--- a/test/lib/spec/expectations/should.rb
+++ /dev/null
@@ -1,5 +0,0 @@
-require 'spec/expectations/should/base'
-require 'spec/expectations/should/have'
-require 'spec/expectations/should/not'
-require 'spec/expectations/should/should'
-require 'spec/expectations/should/change'
diff --git a/test/lib/spec/expectations/should/base.rb b/test/lib/spec/expectations/should/base.rb
deleted file mode 100755
index 1be4677e8..000000000
--- a/test/lib/spec/expectations/should/base.rb
+++ /dev/null
@@ -1,64 +0,0 @@
-module Spec
- module Expectations
- module Should
- class Base
-
- #== and =~ will stay after the new syntax
- def ==(expected)
- __delegate_method_missing_to_target "==", "==", expected
- end
-
- def =~(expected)
- __delegate_method_missing_to_target "=~", "=~", expected
- end
-
- #<, <=, >=, > are all implemented in Spec::Matchers::Be
- # and will be removed with 0.9
- deprecated do
- def <(expected)
- __delegate_method_missing_to_target "<", "<", expected
- end
-
- def <=(expected)
- __delegate_method_missing_to_target "<=", "<=", expected
- end
-
- def >=(expected)
- __delegate_method_missing_to_target ">=", ">=", expected
- end
-
- def >(expected)
- __delegate_method_missing_to_target ">", ">", expected
- end
- end
-
- def default_message(expectation, expected=nil)
- return "expected #{expected.inspect}, got #{@target.inspect} (using #{expectation})" if expectation == '=='
- "expected #{expectation} #{expected.inspect}, got #{@target.inspect}" unless expectation == '=='
- end
-
- def fail_with_message(message, expected=nil, target=nil)
- Spec::Expectations.fail_with(message, expected, target)
- end
-
- def find_supported_sym(original_sym)
- ["#{original_sym}?", "#{original_sym}s?"].each do |alternate_sym|
- return alternate_sym.to_s if @target.respond_to?(alternate_sym.to_s)
- end
- end
-
- deprecated do
- def method_missing(original_sym, *args, &block)
- if original_sym.to_s =~ /^not_/
- return Not.new(@target).__send__(sym, *args, &block)
- end
- if original_sym.to_s =~ /^have_/
- return have.__send__(original_sym.to_s[5..-1].to_sym, *args, &block)
- end
- __delegate_method_missing_to_target original_sym, find_supported_sym(original_sym), *args
- end
- end
- end
- end
- end
-end
diff --git a/test/lib/spec/expectations/should/change.rb b/test/lib/spec/expectations/should/change.rb
deleted file mode 100644
index 98304f1b3..000000000
--- a/test/lib/spec/expectations/should/change.rb
+++ /dev/null
@@ -1,69 +0,0 @@
-module Spec
- module Expectations
- module Should
- class Change < Base
-
- def initialize(target, receiver=nil, message=nil, &block)
- @block = block
- @target = target
- @receiver = receiver
- @message = message
- execute_change
- evaluate_change
- end
-
- def execute_change
- @before_change = @block.nil? ? @receiver.send(@message) : @block.call
- @target.call
- @after_change = @block.nil? ? @receiver.send(@message) : @block.call
- end
-
- def message
- @message.nil? ? 'result' : @message
- end
-
- def evaluate_change
- if @before_change == @after_change
- fail_with_message "#{message} should have changed, but is still #{@after_change.inspect}"
- end
- end
-
- def from(value)
- if @before_change != value
- fail_with_message "#{message} should have initially been #{value.inspect}, but was #{@before_change.inspect}"
- end
- self
- end
-
- def to(value)
- if @after_change != value
- fail_with_message "#{message} should have been changed to #{value.inspect}, but is now #{@after_change.inspect}"
- end
- self
- end
-
- def by(expected_delta)
- if actual_delta != expected_delta
- fail_with_message "#{message} should have been changed by #{expected_delta}, but was changed by #{actual_delta}"
- end
- self
- end
-
- private
- def actual_delta
- @after_change - @before_change
- end
- end
-
- class NotChange < Change
- def evaluate_change
- if @before_change != @after_change
- fail_with_message "#{@message} should not have changed, but did change from #{@before_change.inspect} to #{@after_change.inspect}"
- end
- end
- end
-
- end
- end
-end
-
diff --git a/test/lib/spec/expectations/should/have.rb b/test/lib/spec/expectations/should/have.rb
deleted file mode 100644
index 47ebe81db..000000000
--- a/test/lib/spec/expectations/should/have.rb
+++ /dev/null
@@ -1,128 +0,0 @@
-module Spec
- module Expectations
- module Should
- class Have
- def initialize(target, relativity=:exactly, expected=nil)
- @target = target
- init_collection_handler(target, relativity, expected)
- init_item_handler(target)
- end
-
- def init_collection_handler(target, relativity, expected)
- @collection_handler = CollectionHandler.new(target, relativity, expected)
- end
-
- def init_item_handler(target)
- @item_handler = PositiveItemHandler.new(target)
- end
-
- def method_missing(sym, *args)
- if @collection_handler.wants_to_handle(sym)
- @collection_handler.handle_message(sym, *args)
- elsif @item_handler.wants_to_handle(sym)
- @item_handler.handle_message(sym, *args)
- else
- Spec::Expectations.fail_with("target does not respond to #has_#{sym}?")
- end
- end
- end
-
- class NotHave < Have
- def init_item_handler(target)
- @item_handler = NegativeItemHandler.new(target)
- end
- end
-
- class CollectionHandler
- def initialize(target, relativity=:exactly, expected=nil)
- @target = target
- @expected = expected == :no ? 0 : expected
- @at_least = (relativity == :at_least)
- @at_most = (relativity == :at_most)
- end
-
- def at_least(expected_number=nil)
- @at_least = true
- @at_most = false
- @expected = expected_number == :no ? 0 : expected_number
- self
- end
-
- def at_most(expected_number=nil)
- @at_least = false
- @at_most = true
- @expected = expected_number == :no ? 0 : expected_number
- self
- end
-
- def method_missing(sym, *args)
- if @target.respond_to?(sym)
- handle_message(sym, *args)
- end
- end
-
- def wants_to_handle(sym)
- respond_to?(sym) || @target.respond_to?(sym)
- end
-
- def handle_message(sym, *args)
- return at_least(args[0]) if sym == :at_least
- return at_most(args[0]) if sym == :at_most
- Spec::Expectations.fail_with(build_message(sym, args)) unless as_specified?(sym, args)
- end
-
- def build_message(sym, args)
- message = "expected"
- message += " at least" if @at_least
- message += " at most" if @at_most
- message += " #{@expected} #{sym}, got #{actual_size_of(collection(sym, args))}"
- end
-
- def as_specified?(sym, args)
- return actual_size_of(collection(sym, args)) >= @expected if @at_least
- return actual_size_of(collection(sym, args)) <= @expected if @at_most
- return actual_size_of(collection(sym, args)) == @expected
- end
-
- def collection(sym, args)
- @target.send(sym, *args)
- end
-
- def actual_size_of(collection)
- return collection.length if collection.respond_to? :length
- return collection.size if collection.respond_to? :size
- end
- end
-
- class ItemHandler
- def wants_to_handle(sym)
- @target.respond_to?("has_#{sym}?")
- end
-
- def initialize(target)
- @target = target
- end
-
- def fail_with(message)
- Spec::Expectations.fail_with(message)
- end
- end
-
- class PositiveItemHandler < ItemHandler
- def handle_message(sym, *args)
- fail_with(
- "expected #has_#{sym}?(#{args.collect{|arg| arg.inspect}.join(', ')}) to return true, got false"
- ) unless @target.send("has_#{sym}?", *args)
- end
- end
-
- class NegativeItemHandler < ItemHandler
- def handle_message(sym, *args)
- fail_with(
- "expected #has_#{sym}?(#{args.collect{|arg| arg.inspect}.join(', ')}) to return false, got true"
- ) if @target.send("has_#{sym}?", *args)
- end
- end
- end
- end
-end
diff --git a/test/lib/spec/expectations/should/not.rb b/test/lib/spec/expectations/should/not.rb
deleted file mode 100755
index 5ad530be6..000000000
--- a/test/lib/spec/expectations/should/not.rb
+++ /dev/null
@@ -1,74 +0,0 @@
-module Spec
- module Expectations
- module Should
-
- class Not < Base #:nodoc:
- def initialize(target)
- @target = target
- @be_seen = false
- end
-
- deprecated do
- #Gone for 0.9
- def be(expected = :___no_arg)
- @be_seen = true
- return self if (expected == :___no_arg)
- fail_with_message(default_message("should not be", expected)) if (@target.equal?(expected))
- end
-
- #Gone for 0.9
- def have(expected_number=nil)
- NotHave.new(@target, :exactly, expected_number)
- end
-
- #Gone for 0.9
- def change(receiver, message)
- NotChange.new(@target, receiver, message)
- end
-
- #Gone for 0.9
- def raise(exception=Exception, message=nil)
- begin
- @target.call
- rescue exception => e
- return unless message.nil? || e.message == message || (message.is_a?(Regexp) && e.message =~ message)
- if e.kind_of?(exception)
- failure_message = "expected no "
- failure_message << exception.to_s
- unless message.nil?
- failure_message << " with "
- failure_message << "message matching " if message.is_a?(Regexp)
- failure_message << message.inspect
- end
- failure_message << ", got " << e.inspect
- fail_with_message(failure_message)
- end
- rescue
- true
- end
- end
-
- #Gone for 0.9
- def throw(symbol=:___this_is_a_symbol_that_will_likely_never_occur___)
- begin
- catch symbol do
- @target.call
- return true
- end
- fail_with_message("expected #{symbol.inspect} not to be thrown, but it was")
- rescue NameError
- true
- end
- end
-
- def __delegate_method_missing_to_target original_sym, actual_sym, *args
- ::Spec::Matchers.generated_description = "should not #{original_sym} #{args[0].inspect}"
- return unless @target.__send__(actual_sym, *args)
- fail_with_message(default_message("not #{original_sym}", args[0]))
- end
- end
- end
-
- end
- end
-end
diff --git a/test/lib/spec/expectations/should/should.rb b/test/lib/spec/expectations/should/should.rb
deleted file mode 100755
index cb9f3c4ce..000000000
--- a/test/lib/spec/expectations/should/should.rb
+++ /dev/null
@@ -1,81 +0,0 @@
-module Spec
- module Expectations
- module Should # :nodoc:
-
- class Should < Base
-
- def initialize(target, expectation=nil)
- @target = target
- @be_seen = false
- end
-
- deprecated do
- #Gone for 0.9
- def not
- Not.new(@target)
- end
-
- #Gone for 0.9
- def be(expected = :___no_arg)
- @be_seen = true
- return self if (expected == :___no_arg)
- if Symbol === expected
- fail_with_message(default_message("should be", expected)) unless (@target.equal?(expected))
- else
- fail_with_message("expected #{expected}, got #{@target} (using .equal?)") unless (@target.equal?(expected))
- end
- end
-
- #Gone for 0.9
- def have(expected_number=nil)
- Have.new(@target, :exactly, expected_number)
- end
-
- #Gone for 0.9
- def change(receiver=nil, message=nil, &block)
- Change.new(@target, receiver, message, &block)
- end
-
- #Gone for 0.9
- def raise(exception=Exception, message=nil)
- begin
- @target.call
- rescue exception => e
- unless message.nil?
- if message.is_a?(Regexp)
- e.message.should =~ message
- else
- e.message.should == message
- end
- end
- return
- rescue => e
- fail_with_message("expected #{exception}#{message.nil? ? "" : " with #{message.inspect}"}, got #{e.inspect}")
- end
- fail_with_message("expected #{exception}#{message.nil? ? "" : " with #{message.inspect}"} but nothing was raised")
- end
-
- #Gone for 0.9
- def throw(symbol)
- begin
- catch symbol do
- @target.call
- fail_with_message("expected #{symbol.inspect} to be thrown, but nothing was thrown")
- end
- rescue NameError => e
- fail_with_message("expected #{symbol.inspect} to be thrown, got #{e.inspect}")
- end
- end
- end
-
- private
- def __delegate_method_missing_to_target(original_sym, actual_sym, *args)
- ::Spec::Matchers.generated_description = "should #{original_sym} #{args[0].inspect}"
- return if @target.send(actual_sym, *args)
- fail_with_message(default_message(original_sym, args[0]), args[0], @target)
- end
- end
-
- end
- end
-end
diff --git a/test/lib/spec/expectations/sugar.rb b/test/lib/spec/expectations/sugar.rb
deleted file mode 100644
index 906111f0e..000000000
--- a/test/lib/spec/expectations/sugar.rb
+++ /dev/null
@@ -1,47 +0,0 @@
-deprecated do
-module Spec
- module Expectations
- # This module adds syntactic sugar that allows usage of should_* instead of should.*
- module UnderscoreSugar
- def handle_underscores_for_rspec! # :nodoc:
- original_method_missing = instance_method(:method_missing)
- class_eval do
- def method_missing(sym, *args, &block)
- _method_missing(sym, args, block)
- end
-
- define_method :_method_missing do |sym, args, block|
- return original_method_missing.bind(self).call(sym, *args, &block) unless sym.to_s =~ /^should_/
- if sym.to_s =~ /^should_not_/
- if __matcher.respond_to?(__strip_should_not(sym))
- return should_not(__matcher.__send__(__strip_should_not(sym), *args, &block))
- else
- return Spec::Expectations::Should::Not.new(self).__send__(__strip_should_not(sym), *args, &block) if sym.to_s =~ /^should_not_/
- end
- else
- if __matcher.respond_to?(__strip_should(sym))
- return should(__matcher.__send__(__strip_should(sym), *args, &block))
- else
- return Spec::Expectations::Should::Should.new(self).__send__(__strip_should(sym), *args, &block)
- end
- end
- end
-
- def __strip_should(sym) # :nodoc
- sym.to_s[7..-1]
- end
-
- def __strip_should_not(sym) # :nodoc
- sym.to_s[11..-1]
- end
-
- def __matcher
- @matcher ||= Spec::Matchers::Matcher.new
- end
- end
- end
- end
- end
-end
-
-end \ No newline at end of file
diff --git a/test/lib/spec/matchers/include.rb b/test/lib/spec/matchers/include.rb
deleted file mode 100644
index 0d387f323..000000000
--- a/test/lib/spec/matchers/include.rb
+++ /dev/null
@@ -1,50 +0,0 @@
-module Spec
- module Matchers
-
- class Include #:nodoc:
-
- def initialize(expected)
- @expected = expected
- end
-
- def matches?(actual)
- @actual = actual
- actual.include?(@expected)
- end
-
- def failure_message
- _message
- end
-
- def negative_failure_message
- _message("not ")
- end
-
- def description
- "include #{@expected.inspect}"
- end
-
- private
- def _message(maybe_not="")
- "expected #{@actual.inspect} #{maybe_not}to include #{@expected.inspect}"
- end
- end
-
- # :call-seq:
- # should include(expected)
- # should_not include(expected)
- #
- # Passes if actual includes expected. This works for
- # collections and Strings
- #
- # == Examples
- #
- # [1,2,3].should include(3)
- # [1,2,3].should_not include(4)
- # "spread".should include("read")
- # "spread".should_not include("red")
- def include(expected)
- Matchers::Include.new(expected)
- end
- end
-end
diff --git a/test/lib/spec/matchers/respond_to.rb b/test/lib/spec/matchers/respond_to.rb
deleted file mode 100644
index 013a36f1d..000000000
--- a/test/lib/spec/matchers/respond_to.rb
+++ /dev/null
@@ -1,35 +0,0 @@
-module Spec
- module Matchers
-
- class RespondTo #:nodoc:
- def initialize(sym)
- @sym = sym
- end
-
- def matches?(target)
- return target.respond_to?(@sym)
- end
-
- def failure_message
- "expected target to respond to #{@sym.inspect}"
- end
-
- def negative_failure_message
- "expected target not to respond to #{@sym.inspect}"
- end
-
- def description
- "respond to ##{@sym.to_s}"
- end
- end
-
- # :call-seq:
- # should respond_to(:sym)
- # should_not respond_to(:sym)
- #
- # Matches if the target object responds to :sym
- def respond_to(sym)
- Matchers::RespondTo.new(sym)
- end
- end
-end
diff --git a/test/lib/spec/mocks/methods.rb b/test/lib/spec/mocks/methods.rb
deleted file mode 100644
index a5f102fcf..000000000
--- a/test/lib/spec/mocks/methods.rb
+++ /dev/null
@@ -1,40 +0,0 @@
-module Spec
- module Mocks
- module Methods
- def should_receive(sym, opts={}, &block)
- __mock_handler.add_message_expectation(opts[:expected_from] || caller(1)[0], sym, opts, &block)
- end
-
- def should_not_receive(sym, &block)
- __mock_handler.add_negative_message_expectation(caller(1)[0], sym, &block)
- end
-
- def stub!(sym)
- __mock_handler.add_stub(caller(1)[0], sym)
- end
-
- def received_message?(sym, *args, &block) #:nodoc:
- __mock_handler.received_message?(sym, *args, &block)
- end
-
- def __verify #:nodoc:
- __mock_handler.verify
- end
-
- def __reset_mock #:nodoc:
- __mock_handler.reset
- end
-
- def method_missing(sym, *args, &block) #:nodoc:
- __mock_handler.instance_eval {@messages_received << [sym, args, block]}
- super(sym, *args, &block)
- end
-
- private
-
- def __mock_handler
- @mock_handler ||= MockHandler.new(self, @name, @options)
- end
- end
- end
-end \ No newline at end of file
diff --git a/test/lib/spec/rake/spectask.rb b/test/lib/spec/rake/spectask.rb
deleted file mode 100644
index 5c9b365c1..000000000
--- a/test/lib/spec/rake/spectask.rb
+++ /dev/null
@@ -1,173 +0,0 @@
-#!/usr/bin/env ruby
-
-# Define a task library for running RSpec contexts.
-
-require 'rake'
-require 'rake/tasklib'
-
-module Spec
- module Rake
-
- # A Rake task that runs a set of RSpec contexts.
- #
- # Example:
- #
- # Spec::Rake::SpecTask.new do |t|
- # t.warning = true
- # t.rcov = true
- # end
- #
- # This will create a task that can be run with:
- #
- # rake spec
- #
- class SpecTask < ::Rake::TaskLib
-
- # Name of spec task. (default is :spec)
- attr_accessor :name
-
- # Array of directories to be added to $LOAD_PATH before running the
- # specs. Defaults to ['<the absolute path to RSpec's lib directory>']
- attr_accessor :libs
-
- # If true, requests that the specs be run with the warning flag set.
- # E.g. warning=true implies "ruby -w" used to run the specs. Defaults to false.
- attr_accessor :warning
-
- # Glob pattern to match spec files. (default is 'spec/**/*_spec.rb')
- attr_accessor :pattern
-
- # Array of commandline options to pass to RSpec. Defaults to [].
- attr_accessor :spec_opts
-
- # Where RSpec's output is written. Defaults to STDOUT.
- attr_accessor :out
-
- # Whether or not to use RCov (default is false)
- # See http://eigenclass.org/hiki.rb?rcov
- attr_accessor :rcov
-
- # Array of commandline options to pass to RCov. Defaults to ['--exclude', 'lib\/spec,bin\/spec'].
- # Ignored if rcov=false
- attr_accessor :rcov_opts
-
- # Directory where the RCov report is written. Defaults to "coverage"
- # Ignored if rcov=false
- attr_accessor :rcov_dir
-
- # Array of commandline options to pass to ruby. Defaults to [].
- attr_accessor :ruby_opts
-
- # Whether or not to fail Rake when an error occurs (typically when specs fail).
- # Defaults to true.
- attr_accessor :fail_on_error
-
- # A message to print to stdout when there are failures.
- attr_accessor :failure_message
-
- # Explicitly define the list of spec files to be included in a
- # spec. +list+ is expected to be an array of file names (a
- # FileList is acceptable). If both +pattern+ and +spec_files+ are
- # used, then the list of spec files is the union of the two.
- def spec_files=(list)
- @spec_files = list
- end
-
- # Create a specing task.
- def initialize(name=:spec)
- @name = name
- @libs = [File.expand_path(File.dirname(__FILE__) + '/../../../lib')]
- @pattern = nil
- @spec_files = nil
- @spec_opts = []
- @warning = false
- @ruby_opts = []
- @out = nil
- @fail_on_error = true
- @rcov = false
- @rcov_opts = ['--exclude', 'lib\/spec,bin\/spec,config\/boot.rb']
- @rcov_dir = "coverage"
-
- yield self if block_given?
- @pattern = 'spec/**/*_spec.rb' if @pattern.nil? && @spec_files.nil?
- define
- end
-
- def define
- spec_script = File.expand_path(File.dirname(__FILE__) + '/../../../bin/spec')
-
- lib_path = @libs.join(File::PATH_SEPARATOR)
- actual_name = Hash === name ? name.keys.first : name
- unless ::Rake.application.last_comment
- desc "Run RSpec for #{actual_name}" + (@rcov ? " using RCov" : "")
- end
- task @name do
- RakeFileUtils.verbose(@verbose) do
- ruby_opts = @ruby_opts.clone
- ruby_opts.push( "-I\"#{lib_path}\"" )
- ruby_opts.push( "-S rcov" ) if @rcov
- ruby_opts.push( "-w" ) if @warning
-
- redirect = @out.nil? ? "" : " > \"#{@out}\""
-
- unless spec_file_list.empty?
- # ruby [ruby_opts] -Ilib -S rcov [rcov_opts] bin/spec -- [spec_opts] examples
- # or
- # ruby [ruby_opts] -Ilib bin/spec [spec_opts] examples
- begin
- ruby(
- ruby_opts.join(" ") + " " +
- rcov_option_list +
- (@rcov ? %[ -o "#{@rcov_dir}" ] : "") +
- '"' + spec_script + '"' + " " +
- (@rcov ? "-- " : "") +
- spec_file_list.collect { |fn| %["#{fn}"] }.join(' ') + " " +
- spec_option_list + " " +
- redirect
- )
- rescue => e
- puts @failure_message if @failure_message
- raise e if @fail_on_error
- end
- end
- end
- end
-
- if @rcov
- desc "Remove rcov products for #{actual_name}"
- task paste("clobber_", actual_name) do
- rm_r @rcov_dir rescue nil
- end
-
- clobber_task = paste("clobber_", actual_name)
- task :clobber => [clobber_task]
-
- task actual_name => clobber_task
- end
- self
- end
-
- def rcov_option_list # :nodoc:
- return "" unless @rcov
- ENV['RCOVOPTS'] || @rcov_opts.join(" ") || ""
- end
-
- def spec_option_list # :nodoc:
- ENV['RSPECOPTS'] || @spec_opts.join(" ") || ""
- end
-
- def spec_file_list # :nodoc:
- if ENV['SPEC']
- FileList[ ENV['SPEC'] ]
- else
- result = []
- result += @spec_files.to_a if @spec_files
- result += FileList[ @pattern ].to_a if @pattern
- FileList[result]
- end
- end
-
- end
- end
-end
-
diff --git a/test/lib/spec/runner.rb b/test/lib/spec/runner.rb
deleted file mode 100644
index 976802bd1..000000000
--- a/test/lib/spec/runner.rb
+++ /dev/null
@@ -1,132 +0,0 @@
-require 'spec/runner/formatter'
-require 'spec/runner/context'
-require 'spec/runner/context_eval'
-require 'spec/runner/specification'
-require 'spec/runner/execution_context'
-require 'spec/runner/context_runner'
-require 'spec/runner/option_parser'
-require 'spec/runner/command_line'
-require 'spec/runner/drb_command_line'
-require 'spec/runner/backtrace_tweaker'
-require 'spec/runner/reporter'
-require 'spec/runner/spec_matcher'
-require 'spec/runner/extensions/object'
-require 'spec/runner/extensions/kernel'
-require 'spec/runner/spec_should_raise_handler'
-require 'spec/runner/spec_parser'
-
-module Spec
- # == Contexts and Specifications
- #
- # Rather than expressing examples in classes, RSpec uses a custom domain specific language to express
- # examples using contexts and specifications.
- #
- # A context is the equivalent of a fixture in xUnit-speak. It is a metaphor for the context
- # in which you will run your executable example - a set of known objects in a known starting state.
- #
- # context "A new account" do
- #
- # setup do
- # @account = Account.new
- # end
- #
- # specify "should have a balance of $0" do
- # @account.balance.should_eql Money.new(0, :dollars)
- # end
- #
- # end
- #
- # We use the setup block to set up the context (given), and then the specify method to
- # hold the example code that expresses the event (when) and the expected outcome (then).
- #
- # == Helper Methods
- #
- # A primary goal of RSpec is to keep the examples clear. We therefore prefer
- # less indirection than you might see in xUnit examples and in well factored, DRY production code. We feel
- # that duplication is OK if removing it makes it harder to understand an example without
- # having to look elsewhere to understand its context.
- #
- # That said, RSpec does support some level of encapsulating common code in helper
- # methods that can exist within a context or within an included module.
- #
- # == Setup and Teardown
- #
- # You can use setup, teardown, context_setup and context_teardown within a context:
- #
- # context "..." do
- # context_setup do
- # ...
- # end
- #
- # setup do
- # ...
- # end
- #
- # specify "number one" do
- # ...
- # end
- #
- # specify "number two" do
- # ...
- # end
- #
- # teardown do
- # ...
- # end
- #
- # context_teardown do
- # ...
- # end
- #
- # end
- #
- # The <tt>setup</tt> block will run before each of the specs, once for each spec. Likewise,
- # the <tt>teardown</tt> block will run after each of the specs.
- #
- # It is also possible to specify a <tt>context_setup</tt> and <tt>context_teardown</tt>
- # block that will run only once for each context, respectively before the first <code>setup</code>
- # and after the last <code>teardown</code>. The use of these is generally discouraged, because it
- # introduces dependencies between the specs. Still, it might prove useful for very expensive operations
- # if you know what you are doing.
- #
- # == Local helper methods
- #
- # You can include local helper methods by simply expressing them within a context:
- #
- # context "..." do
- #
- # specify "..." do
- # helper_method
- # end
- #
- # def helper_method
- # ...
- # end
- #
- # end
- #
- # == Included helper methods
- #
- # You can include helper methods in multiple contexts by expressing them within
- # a module, and then including that module in your context:
- #
- # module AccountExampleHelperMethods
- # def helper_method
- # ...
- # end
- # end
- #
- # context "A new account" do
- # include AccountExampleHelperMethods
- # setup do
- # @account = Account.new
- # end
- #
- # specify "should have a balance of $0" do
- # helper_method
- # @account.balance.should eql(Money.new(0, :dollars))
- # end
- # end
- module Runner
- end
-end
diff --git a/test/lib/spec/runner/command_line.rb b/test/lib/spec/runner/command_line.rb
deleted file mode 100644
index db928ad9b..000000000
--- a/test/lib/spec/runner/command_line.rb
+++ /dev/null
@@ -1,34 +0,0 @@
-require 'spec/runner/option_parser'
-
-module Spec
- module Runner
- # Facade to run specs without having to fork a new ruby process (using `spec ...`)
- class CommandLine
- # Runs specs. +argv+ is the commandline args as per the spec commandline API, +err+
- # and +out+ are the streams output will be written to. +exit+ tells whether or
- # not a system exit should be called after the specs are run and
- # +warn_if_no_files+ tells whether or not a warning (the help message)
- # should be printed to +err+ in case no files are specified.
- def self.run(argv, err, out, exit=true, warn_if_no_files=true)
- old_context_runner = defined?($context_runner) ? $context_runner : nil
- $context_runner = OptionParser.new.create_context_runner(argv, err, out, warn_if_no_files)
- return if $context_runner.nil? # This is the case if we use --drb
-
- # If ARGV is a glob, it will actually each over each one of the matching files.
- argv.each do |file_or_dir|
- if File.directory?(file_or_dir)
- Dir["#{file_or_dir}/**/*.rb"].each do |file|
- load file
- end
- elsif File.file?(file_or_dir)
- load file_or_dir
- else
- raise "File or directory not found: #{file_or_dir}"
- end
- end
- $context_runner.run(exit)
- $context_runner = old_context_runner
- end
- end
- end
-end \ No newline at end of file
diff --git a/test/lib/spec/runner/context.rb b/test/lib/spec/runner/context.rb
deleted file mode 100644
index 3155e169a..000000000
--- a/test/lib/spec/runner/context.rb
+++ /dev/null
@@ -1,154 +0,0 @@
-module Spec
- module Runner
- class ContextEvalModule < Module
- end
- class Context
- module InstanceMethods
- def initialize(description, &context_block)
- @description = description
-
- @context_eval_module = ContextEvalModule.new
- @context_eval_module.extend ContextEval::ModuleMethods
- @context_eval_module.include ContextEval::InstanceMethods
- before_context_eval
- @context_eval_module.class_eval(&context_block)
- end
-
- def before_context_eval
- end
-
- def inherit_context_eval_module_from(klass)
- @context_eval_module.inherit klass
- end
- alias :inherit :inherit_context_eval_module_from
-
- def include(mod)
- @context_eval_module.include(mod)
- end
-
- def run(reporter, dry_run=false)
- reporter.add_context(@description)
- prepare_execution_context_class
- errors = run_context_setup(reporter, dry_run)
-
- specifications.each do |specification|
- specification_execution_context = execution_context(specification)
- specification_execution_context.copy_instance_variables_from(@once_only_execution_context_instance, []) unless context_setup_block.nil?
- specification.run(reporter, setup_block, teardown_block, dry_run, specification_execution_context)
- end unless errors.length > 0
-
- run_context_teardown(reporter, dry_run)
- end
-
- def number_of_specs
- specifications.length
- end
-
- def matches?(full_description)
- matcher ||= SpecMatcher.new(@description)
- specifications.each do |spec|
- return true if spec.matches?(matcher, full_description)
- end
- return false
- end
-
- def run_single_spec(full_description)
- return if @description == full_description
- matcher = SpecMatcher.new(@description)
- specifications.reject! do |spec|
- !spec.matches?(matcher, full_description)
- end
- end
-
- def methods
- my_methods = super
- my_methods |= @context_eval_module.methods
- my_methods
- end
-
- protected
-
- def method_missing(*args)
- @context_eval_module.method_missing(*args)
- end
-
- def context_setup_block
- @context_eval_module.send :context_setup_block
- end
-
- def context_teardown_block
- @context_eval_module.send :context_teardown_block
- end
-
- def specifications
- @context_eval_module.send :specifications
- end
-
- def setup_block
- @context_eval_module.send :setup_block
- end
-
- def teardown_block
- @context_eval_module.send :teardown_block
- end
-
- def prepare_execution_context_class
- weave_in_context_modules
- execution_context_class
- end
-
- def weave_in_context_modules
- mods = context_modules
- context_eval_module = @context_eval_module
- execution_context_class.class_eval do
- include context_eval_module
- mods.each do |mod|
- include mod
- end
- end
- end
-
- def context_modules
- @context_eval_module.send :context_modules
- end
-
- def execution_context_class
- @context_eval_module.send :execution_context_class
- end
-
- def execution_context specification
- execution_context_class.new(specification)
- end
-
- def run_context_setup(reporter, dry_run)
- errors = []
- unless dry_run
- begin
- @once_only_execution_context_instance = execution_context(nil)
- @once_only_execution_context_instance.instance_eval(&context_setup_block)
- rescue => e
- errors << e
- location = "context_setup"
- reporter.spec_finished(location, e, location) if reporter
- end
- end
- errors
- end
-
- def run_context_teardown(reporter, dry_run)
- unless dry_run
- begin
- @once_only_execution_context_instance ||= execution_context(nil)
- @once_only_execution_context_instance.instance_eval(&context_teardown_block)
- rescue => e
- location = "context_teardown"
- reporter.spec_finished(location, e, location) if reporter
- end
- end
- end
-
- end
- include InstanceMethods
- end
- end
-end
diff --git a/test/lib/spec/runner/context_eval.rb b/test/lib/spec/runner/context_eval.rb
deleted file mode 100644
index 2cee8f1cd..000000000
--- a/test/lib/spec/runner/context_eval.rb
+++ /dev/null
@@ -1,142 +0,0 @@
-module Spec
- module Runner
- module ContextEval
- module ModuleMethods
- def inherit(klass)
- @context_superclass = klass
- derive_execution_context_class_from_context_superclass
- end
-
- def include(mod)
- context_modules << mod
- mod.send :included, self
- end
-
- def context_setup(&block)
- context_setup_parts << block
- end
-
- def context_teardown(&block)
- context_teardown_parts << block
- end
-
- def setup(&block)
- setup_parts << block
- end
-
- def teardown(&block)
- teardown_parts << block
- end
-
- def specify(spec_name=:__generate_description, opts={}, &block)
- specifications << Specification.new(spec_name, opts, &block)
- end
-
- def methods
- my_methods = super
- my_methods |= context_superclass.methods
- my_methods
- end
-
- protected
-
- def method_missing(method_name, *args)
- if context_superclass.respond_to?(method_name)
- return execution_context_class.send(method_name, *args)
- end
- super
- end
-
- private
-
- def context_setup_block
- parts = context_setup_parts.dup
- add_context_superclass_method(:context_setup, parts)
- create_block_from_parts(parts)
- end
-
- def context_teardown_block
- parts = context_teardown_parts.dup
- add_context_superclass_method(:context_teardown, parts)
- create_block_from_parts(parts)
- end
-
- def setup_block
- parts = setup_parts.dup
- add_context_superclass_method(:setup, parts)
- create_block_from_parts(parts)
- end
-
- def teardown_block
- parts = teardown_parts.dup
- add_context_superclass_method(:teardown, parts)
- create_block_from_parts(parts)
- end
-
- def execution_context_class
- @execution_context_class ||= derive_execution_context_class_from_context_superclass
- end
-
- def derive_execution_context_class_from_context_superclass
- @execution_context_class = Class.new(context_superclass)
- @execution_context_class.class_eval do
- include ::Spec::Runner::ExecutionContext::InstanceMethods
- end
- end
-
- def context_superclass
- @context_superclass ||= Object
- end
-
- def context_modules
- @context_modules ||= [Spec::Matchers, Spec::Mocks]
- end
-
- def specifications
- @specifications ||= []
- end
-
- def context_setup_parts
- @context_setup_parts ||= []
- end
-
- def context_teardown_parts
- @context_teardown_parts ||= []
- end
-
- def setup_parts
- @setup_parts ||= []
- end
-
- def teardown_parts
- @teardown_parts ||= []
- end
-
- def add_context_superclass_method sym, parts
- superclass_method = begin
- context_superclass.instance_method(sym)
- rescue
- nil
- end
- parts.unshift superclass_method if superclass_method
- end
-
- def create_block_from_parts(parts)
- proc do
- parts.each do |part|
- if part.is_a?(UnboundMethod)
- part.bind(self).call
- else
- instance_eval(&part)
- end
- end
- end
- end
- end
-
- module InstanceMethods
- end
-
- end
- end
-end
diff --git a/test/lib/spec/runner/context_runner.rb b/test/lib/spec/runner/context_runner.rb
deleted file mode 100644
index 0a4d7e6e9..000000000
--- a/test/lib/spec/runner/context_runner.rb
+++ /dev/null
@@ -1,55 +0,0 @@
-module Spec
- module Runner
- class ContextRunner
-
- def initialize(options)
- @contexts = []
- @options = options
- end
-
- def add_context(context)
- return unless spec_description.nil? || context.matches?(spec_description)
- context.run_single_spec(spec_description) if context.matches?(spec_description)
- @contexts << context
- end
-
- # Runs all contexts and returns the number of failures.
- def run(exit_when_done)
- @options.reporter.start(number_of_specs)
- begin
- @contexts.each do |context|
- context.run(@options.reporter, @options.dry_run)
- end
- rescue Interrupt
- ensure
- @options.reporter.end
- end
- failure_count = @options.reporter.dump
-
- if(failure_count == 0 && !@options.heckle_runner.nil?)
- heckle_runner = @options.heckle_runner
- @options.heckle_runner = nil
- context_runner = self.class.new(@options)
- context_runner.instance_variable_set(:@contexts, @contexts)
- heckle_runner.heckle_with(context_runner)
- end
-
- if(exit_when_done)
- exit_code = (failure_count == 0) ? 0 : 1
- exit(exit_code)
- end
- failure_count
- end
-
- def number_of_specs
- @contexts.inject(0) {|sum, context| sum + context.number_of_specs}
- end
-
- private
- def spec_description
- @options.spec_name
- end
-
- end
- end
-end
diff --git a/test/lib/spec/runner/execution_context.rb b/test/lib/spec/runner/execution_context.rb
deleted file mode 100644
index 484c55830..000000000
--- a/test/lib/spec/runner/execution_context.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-module Spec
- module Runner
- class ExecutionContext
- module InstanceMethods
- def initialize(*args) #:nodoc:
- #necessary for RSpec's own specs
- end
-
- def violated(message="")
- raise Spec::Expectations::ExpectationNotMetError.new(message)
- end
-
- end
- include InstanceMethods
- end
- end
-end \ No newline at end of file
diff --git a/test/lib/spec/runner/extensions/kernel.rb b/test/lib/spec/runner/extensions/kernel.rb
deleted file mode 100644
index f060ec859..000000000
--- a/test/lib/spec/runner/extensions/kernel.rb
+++ /dev/null
@@ -1,17 +0,0 @@
-module Kernel
- def context(name, &block)
- context = Spec::Runner::Context.new(name, &block)
- context_runner.add_context(context)
- end
-
-private
-
- def context_runner
- # TODO: Figure out a better way to get this considered "covered" and keep this statement on multiple lines
- unless $context_runner; \
- $context_runner = ::Spec::Runner::OptionParser.new.create_context_runner(ARGV.dup, STDERR, STDOUT, false); \
- at_exit { $context_runner.run(false) }; \
- end
- $context_runner
- end
-end
diff --git a/test/lib/spec/runner/formatter/base_text_formatter.rb b/test/lib/spec/runner/formatter/base_text_formatter.rb
deleted file mode 100644
index 31d1c3132..000000000
--- a/test/lib/spec/runner/formatter/base_text_formatter.rb
+++ /dev/null
@@ -1,118 +0,0 @@
-module Spec
- module Runner
- module Formatter
- # Baseclass for text-based formatters. Can in fact be used for
- # non-text based ones too - just ignore the +output+ constructor
- # argument.
- class BaseTextFormatter
- def initialize(output, dry_run=false, colour=false)
- @output = output
- @dry_run = dry_run
- @colour = colour
- begin ; require 'Win32/Console/ANSI' if @colour && PLATFORM =~ /win32/ ; rescue LoadError ; raise "You must gem install win32console to use colour on Windows" ; end
- end
-
- # This method is invoked before any specs are run, right after
- # they have all been collected. This can be useful for special
- # formatters that need to provide progress on feedback (graphical ones)
- #
- # This method will only be invoked once, and the next one to be invoked
- # is #add_context
- def start(spec_count)
- end
-
- # This method is invoked at the beginning of the execution of each context.
- # +name+ is the name of the context and +first+ is true if it is the
- # first context - otherwise it's false.
- #
- # The next method to be invoked after this is #spec_started
- def add_context(name, first)
- end
-
- # This method is invoked right before a spec is executed.
- # The next method to be invoked after this one is one of #spec_failed
- # or #spec_passed.
- def spec_started(name)
- end
-
- # This method is invoked when a spec fails, i.e. an exception occurred
- # inside it (such as a failed should or other exception). +name+ is the name
- # of the specification. +counter+ is the sequence number of the failure
- # (starting at 1) and +failure+ is the associated Failure object.
- def spec_failed(name, counter, failure)
- end
-
- # This method is invoked when a spec passes. +name+ is the name of the
- # specification.
- def spec_passed(name)
- end
-
- # This method is invoked after all of the specs have executed. The next method
- # to be invoked after this one is #dump_failure (once for each failed spec),
- def start_dump
- end
-
- # Dumps detailed information about a spec failure.
- # This method is invoked for each failed spec after all specs have run. +counter+ is the sequence number
- # of the associated spec. +failure+ is a Failure object, which contains detailed
- # information about the failure.
- def dump_failure(counter, failure)
- @output.puts
- @output.puts "#{counter.to_s})"
- if(failure.expectation_not_met?)
- @output.puts red(failure.header)
- @output.puts red(failure.exception.message)
- else
- @output.puts magenta(failure.header)
- @output.puts magenta(failure.exception.message)
- end
- @output.puts format_backtrace(failure.exception.backtrace)
- STDOUT.flush
- end
-
- # This method is invoked at the very end.
- def dump_summary(duration, spec_count, failure_count)
- return if @dry_run
- @output.puts
- @output.puts "Finished in #{duration} seconds"
- @output.puts
- summary = "#{spec_count} specification#{'s' unless spec_count == 1}, #{failure_count} failure#{'s' unless failure_count == 1}"
- if failure_count == 0
- @output.puts green(summary)
- else
- @output.puts red(summary)
- end
- end
-
- def format_backtrace(backtrace)
- return "" if backtrace.nil?
- backtrace.map { |line| backtrace_line(line) }.join("\n")
- end
-
- protected
-
- def backtrace_line(line)
- line.sub(/\A([^:]+:\d+)$/, '\\1:')
- end
-
- def colour(text, colour_code)
- return text unless @colour && output_to_tty?
- "#{colour_code}#{text}\e[0m"
- end
-
- def output_to_tty?
- begin
- @output == Kernel || @output.tty?
- rescue NoMethodError
- false
- end
- end
-
- def red(text); colour(text, "\e[31m"); end
- def green(text); colour(text, "\e[32m"); end
- def magenta(text); colour(text, "\e[35m"); end
-
- end
- end
- end
-end
diff --git a/test/lib/spec/runner/formatter/html_formatter.rb b/test/lib/spec/runner/formatter/html_formatter.rb
deleted file mode 100644
index 13b796581..000000000
--- a/test/lib/spec/runner/formatter/html_formatter.rb
+++ /dev/null
@@ -1,219 +0,0 @@
-module Spec
- module Runner
- module Formatter
- class HtmlFormatter < BaseTextFormatter
- attr_reader :current_spec_number, :current_context_number
-
- def initialize(output, dry_run=false, colour=false)
- super
- @current_spec_number = 0
- @current_context_number = 0
- end
-
- def start(spec_count)
- @spec_count = spec_count
-
- @output.puts HEADER_1
- @output.puts extra_header_content unless extra_header_content.nil?
- @output.puts HEADER_2
- STDOUT.flush
- end
-
- def add_context(name, first)
- @current_context_number += 1
- unless first
- @output.puts " </dl>"
- @output.puts "</div>"
- end
- @output.puts "<div class=\"context\">"
- @output.puts " <dl>"
- @output.puts " <dt id=\"context_#{@current_context_number}\">#{name}</dt>"
- STDOUT.flush
- end
-
- def start_dump
- @output.puts " </dl>"
- @output.puts "</div>"
- STDOUT.flush
- end
-
- def spec_started(name)
- @current_spec_number += 1
- STDOUT.flush
- end
-
- def spec_passed(name)
- move_progress
- @output.puts " <dd class=\"spec passed\"><span class=\"passed_spec_name\">#{escape(name)}</span></dd>"
- STDOUT.flush
- end
-
- def spec_failed(name, counter, failure)
- @output.puts " <script type=\"text/javascript\">makeRed('header');</script>"
- @output.puts " <script type=\"text/javascript\">makeRed('context_#{@current_context_number}');</script>"
- move_progress
- @output.puts " <dd class=\"spec failed\">"
- @output.puts " <span class=\"failed_spec_name\">#{escape(name)}</span>"
- @output.puts " <div class=\"failure\" id=\"failure_#{counter}\">"
- @output.puts " <div class=\"message\"><pre>#{escape(failure.exception.message)}</pre></div>" unless failure.exception.nil?
- @output.puts " <div class=\"backtrace\"><pre>#{format_backtrace(failure.exception.backtrace)}</pre></div>" unless failure.exception.nil?
- @output.puts extra_failure_content unless extra_failure_content.nil?
- @output.puts " </div>"
- @output.puts " </dd>"
- STDOUT.flush
- end
-
- # Override this method if you wish to output extra HTML in the header
- #
- def extra_header_content
- end
-
- # Override this method if you wish to output extra HTML for a failed spec. For example, you
- # could output links to images or other files produced during the specs.
- #
- def extra_failure_content
- end
-
- def move_progress
- percent_done = @spec_count == 0 ? 100.0 : (@current_spec_number.to_f / @spec_count.to_f * 1000).to_i / 10.0
- @output.puts " <script type=\"text/javascript\">moveProgressBar('#{percent_done}');</script>"
- end
-
- def escape(string)
- string.gsub(/&/n, '&amp;').gsub(/\"/n, '&quot;').gsub(/>/n, '&gt;').gsub(/</n, '&lt;')
- end
-
- def dump_failure(counter, failure)
- end
-
- def dump_summary(duration, spec_count, failure_count)
- if @dry_run
- totals = "This was a dry-run"
- else
- totals = "#{spec_count} specification#{'s' unless spec_count == 1}, #{failure_count} failure#{'s' unless failure_count == 1}"
- end
- @output.puts "<script type=\"text/javascript\">document.getElementById('duration').innerHTML = \"Finished in <strong>#{duration} seconds</strong>\";</script>"
- @output.puts "<script type=\"text/javascript\">document.getElementById('totals').innerHTML = \"#{totals}\";</script>"
- @output.puts "</div>"
- @output.puts "</body>"
- @output.puts "</html>"
- STDOUT.flush
- end
-
- HEADER_1 = <<-EOF
-<?xml version="1.0" encoding="iso-8859-1"?>
-<!DOCTYPE html
- PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
- "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
- <title>RSpec results</title>
- <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
- <meta http-equiv="Expires" content="-1" />
- <meta http-equiv="Pragma" content="no-cache" />
-EOF
-
- HEADER_2 = <<-EOF
- <script type="text/javascript">
- function moveProgressBar(percentDone) {
- document.getElementById("header").style.width = percentDone +"%";
- }
- function makeRed(element_id) {
- document.getElementById(element_id).style.background = '#C40D0D';
- }
- </script>
- <style type="text/css">
- body {
- margin: 0; padding: 0;
- background: #fff;
- }
-
- #header {
- background: #65C400; color: #fff;
- }
-
- h1 {
- margin: 0 0 10px;
- padding: 10px;
- font: bold 18px "Lucida Grande", Helvetica, sans-serif;
- }
-
- #summary {
- margin: 0; padding: 5px 10px;
- font: bold 10px "Lucida Grande", Helvetica, sans-serif;
- text-align: right;
- position: absolute;
- top: 0px;
- right: 0px;
- }
-
- #summary p {
- margin: 0 0 2px;
- }
-
- #summary #totals {
- font-size: 14px;
- }
-
- .context {
- margin: 0 10px 5px;
- background: #fff;
- }
-
- dl {
- margin: 0; padding: 0 0 5px;
- font: normal 11px "Lucida Grande", Helvetica, sans-serif;
- }
-
- dt {
- padding: 3px;
- background: #65C400;
- color: #fff;
- font-weight: bold;
- }
-
- dd {
- margin: 5px 0 5px 5px;
- padding: 3px 3px 3px 18px;
- }
-
- dd.spec.passed {
- border-left: 5px solid #65C400;
- border-bottom: 1px solid #65C400;
- background: #DBFFB4; color: #3D7700;
- }
-
- dd.spec.failed {
- border-left: 5px solid #C20000;
- border-bottom: 1px solid #C20000;
- color: #C20000; background: #FFFBD3;
- }
-
- div.backtrace {
- color: #000;
- font-size: 12px;
- }
-
- a {
- color: #BE5C00;
- }
- </style>
-</head>
-<body>
-
-<div id="header">
- <h1>RSpec Results</h1>
-
- <div id="summary">
- <p id="duration">&nbsp;</p>
- <p id="totals">&nbsp;</p>
- </div>
-</div>
-
-<div id="results">
-EOF
- end
- end
- end
-end
diff --git a/test/lib/spec/runner/formatter/progress_bar_formatter.rb b/test/lib/spec/runner/formatter/progress_bar_formatter.rb
deleted file mode 100644
index fe519d4d8..000000000
--- a/test/lib/spec/runner/formatter/progress_bar_formatter.rb
+++ /dev/null
@@ -1,27 +0,0 @@
-module Spec
- module Runner
- module Formatter
- class ProgressBarFormatter < BaseTextFormatter
- def add_context(name, first)
- @output.puts if first
- STDOUT.flush
- end
-
- def spec_failed(name, counter, failure)
- @output.print failure.expectation_not_met? ? red('F') : magenta('F')
- STDOUT.flush
- end
-
- def spec_passed(name)
- @output.print green('.')
- STDOUT.flush
- end
-
- def start_dump
- @output.puts
- STDOUT.flush
- end
- end
- end
- end
-end \ No newline at end of file
diff --git a/test/lib/spec/runner/formatter/rdoc_formatter.rb b/test/lib/spec/runner/formatter/rdoc_formatter.rb
deleted file mode 100644
index eae55c3ea..000000000
--- a/test/lib/spec/runner/formatter/rdoc_formatter.rb
+++ /dev/null
@@ -1,22 +0,0 @@
-module Spec
- module Runner
- module Formatter
- class RdocFormatter < BaseTextFormatter
- def add_context(name, first)
- @output.print "# #{name}\n"
- STDOUT.flush
- end
-
- def spec_passed(name)
- @output.print "# * #{name}\n"
- STDOUT.flush
- end
-
- def spec_failed(name, counter, failure)
- @output.print "# * #{name} [#{counter} - FAILED]\n"
- STDOUT.flush
- end
- end
- end
- end
-end \ No newline at end of file
diff --git a/test/lib/spec/runner/formatter/specdoc_formatter.rb b/test/lib/spec/runner/formatter/specdoc_formatter.rb
deleted file mode 100644
index 67b4312bf..000000000
--- a/test/lib/spec/runner/formatter/specdoc_formatter.rb
+++ /dev/null
@@ -1,23 +0,0 @@
-module Spec
- module Runner
- module Formatter
- class SpecdocFormatter < BaseTextFormatter
- def add_context(name, first)
- @output.puts
- @output.puts name
- STDOUT.flush
- end
-
- def spec_failed(name, counter, failure)
- @output.puts failure.expectation_not_met? ? red("- #{name} (FAILED - #{counter})") : magenta("- #{name} (ERROR - #{counter})")
- STDOUT.flush
- end
-
- def spec_passed(name)
- @output.print green("- #{name}\n")
- STDOUT.flush
- end
- end
- end
- end
-end \ No newline at end of file
diff --git a/test/lib/spec/runner/option_parser.rb b/test/lib/spec/runner/option_parser.rb
deleted file mode 100644
index 38725d848..000000000
--- a/test/lib/spec/runner/option_parser.rb
+++ /dev/null
@@ -1,224 +0,0 @@
-require 'ostruct'
-require 'optparse'
-require 'spec/runner/spec_parser'
-require 'spec/runner/formatter'
-require 'spec/runner/backtrace_tweaker'
-require 'spec/runner/reporter'
-require 'spec/runner/context_runner'
-
-module Spec
- module Runner
- class OptionParser
- def initialize
- @spec_parser = SpecParser.new
- @file_factory = File
- end
-
- def create_context_runner(args, err, out, warn_if_no_files)
- options = parse(args, err, out, warn_if_no_files)
- # Some exit points in parse (--generate-options, --drb) don't return the options,
- # but hand over control. In that case we don't want to continue.
- return nil unless options.is_a?(OpenStruct)
-
- formatter = options.formatter_type.new(options.out, options.dry_run, options.colour)
- options.reporter = Reporter.new(formatter, options.backtrace_tweaker)
-
- # this doesn't really belong here.
- # it should, but the way things are coupled, it doesn't
- if options.differ_class
- Spec::Expectations.differ = options.differ_class.new(options.diff_format, options.context_lines, options.colour)
- end
-
- unless options.generate
- ContextRunner.new(options)
- end
- end
-
- def parse(args, err, out, warn_if_no_files)
- options_file = nil
- args_copy = args.dup
- options = OpenStruct.new
- options.out = (out == STDOUT ? Kernel : out)
- options.formatter_type = Formatter::ProgressBarFormatter
- options.backtrace_tweaker = QuietBacktraceTweaker.new
- options.spec_name = nil
-
- opts = ::OptionParser.new do |opts|
- opts.banner = "Usage: spec [options] (FILE|DIRECTORY|GLOB)+"
- opts.separator ""
-
- opts.on("-D", "--diff [FORMAT]", "Show diff of objects that are expected to be equal when they are not",
- "Builtin formats: unified|u|context|c",
- "You can also specify a custom differ class",
- "(in which case you should also specify --require)") do |format|
-
- # TODO make context_lines settable
- options.context_lines = 3
-
- case format
- when 'context', 'c'
- options.diff_format = :context
- when 'unified', 'u', '', nil
- options.diff_format = :unified
- end
-
- if [:context,:unified].include? options.diff_format
- require 'spec/expectations/differs/default'
- options.differ_class = Spec::Expectations::Differs::Default
- else
- begin
- options.diff_format = :custom
- options.differ_class = eval(format)
- rescue NameError
- err.puts "Couldn't find differ class #{format}"
- err.puts "Make sure the --require option is specified *before* --diff"
- exit if out == $stdout
- end
- end
-
- end
-
- opts.on("-c", "--colour", "--color", "Show coloured (red/green) output") do
- options.colour = true
- end
-
- opts.on("-s", "--spec SPECIFICATION_NAME", "Execute context or specification with matching name") do |spec_name|
- options.spec_name = spec_name
- end
-
- opts.on("-l", "--line LINE_NUMBER", Integer, "Execute context or specification at given line") do |line_number|
- options.line_number = line_number.to_i
- end
-
- opts.on("-f", "--format FORMAT", "Builtin formats: specdoc|s|rdoc|r|html|h",
- "You can also specify a custom formatter class",
- "(in which case you should also specify --require)") do |format|
- case format
- when 'specdoc', 's'
- options.formatter_type = Formatter::SpecdocFormatter
- when 'html', 'h'
- options.formatter_type = Formatter::HtmlFormatter
- when 'rdoc', 'r'
- options.formatter_type = Formatter::RdocFormatter
- options.dry_run = true
- else
- begin
- options.formatter_type = eval(format)
- rescue NameError
- err.puts "Couldn't find formatter class #{format}"
- err.puts "Make sure the --require option is specified *before* --format"
- exit if out == $stdout
- end
- end
- end
-
- opts.on("-r", "--require FILE", "Require FILE before running specs",
- "Useful for loading custom formatters or other extensions",
- "If this option is used it must come before the others") do |req|
- req.split(",").each{|file| require file}
- end
-
- opts.on("-b", "--backtrace", "Output full backtrace") do
- options.backtrace_tweaker = NoisyBacktraceTweaker.new
- end
-
- opts.on("-H", "--heckle CODE", "If all specs pass, this will run your specs many times, mutating",
- "the specced code a little each time. The intent is that specs",
- "*should* fail, and RSpec will tell you if they don't.",
- "CODE should be either Some::Module, Some::Class or Some::Fabulous#method}") do |heckle|
- heckle_runner = PLATFORM == 'i386-mswin32' ? 'spec/runner/heckle_runner_win' : 'spec/runner/heckle_runner'
- require heckle_runner
- options.heckle_runner = HeckleRunner.new(heckle)
- end
-
- opts.on("-d", "--dry-run", "Don't execute specs") do
- options.dry_run = true
- end
-
- opts.on("-o", "--out OUTPUT_FILE", "Path to output file (defaults to STDOUT)") do |out_file|
- options.out = File.new(out_file, 'w')
- end
-
- opts.on("-O", "--options PATH", "Read options from a file") do |options_file|
- # Remove the --options option and the argument before writing to file
- index = args_copy.index("-O") || args_copy.index("--options")
- args_copy.delete_at(index)
- args_copy.delete_at(index)
-
- new_args = args_copy + IO.readlines(options_file).each {|s| s.chomp!}
- return CommandLine.run(new_args, err, out, true, warn_if_no_files)
- end
-
- opts.on("-G", "--generate-options PATH", "Generate an options file for --options") do |options_file|
- # Remove the --generate-options option and the argument before writing to file
- index = args_copy.index("-G") || args_copy.index("--generate-options")
- args_copy.delete_at(index)
- args_copy.delete_at(index)
-
- File.open(options_file, 'w') do |io|
- io.puts args_copy.join("\n")
- end
- out.puts "\nOptions written to #{options_file}. You can now use these options with:"
- out.puts "spec --options #{options_file}"
- options.generate = true
- end
-
- opts.on("-X", "--drb", "Run specs via DRb. (For example against script/rails_spec_server)") do |options_file|
- # Remove the --options option and the argument before writing to file
- index = args_copy.index("-X") || args_copy.index("--drb")
- args_copy.delete_at(index)
-
- return DrbCommandLine.run(args_copy, err, out, true, warn_if_no_files)
- end
-
- opts.on("-v", "--version", "Show version") do
- out.puts ::Spec::VERSION::DESCRIPTION
- exit if out == $stdout
- end
-
- opts.on_tail("-h", "--help", "You're looking at it") do
- out.puts opts
- exit if out == $stdout
- end
-
- end
- opts.parse!(args)
-
- if args.empty? && warn_if_no_files
- err.puts "No files specified."
- err.puts opts
- exit(6) if err == $stderr
- end
-
- if options.line_number
- set_spec_from_line_number(options, args, err)
- end
-
- options
- end
-
- def set_spec_from_line_number(options, args, err)
- unless options.spec_name
- if args.length == 1
- if @file_factory.file?(args[0])
- source = @file_factory.open(args[0])
- options.spec_name = @spec_parser.spec_name_for(source, options.line_number)
- elsif @file_factory.directory?(args[0])
- err.puts "You must specify one file, not a directory when using the --line option"
- exit(1) if err == $stderr
- else
- err.puts "#{args[0]} does not exist"
- exit(2) if err == $stderr
- end
- else
- err.puts "Only one file can be specified when using the --line option: #{args.inspect}"
- exit(3) if err == $stderr
- end
- else
- err.puts "You cannot use both --line and --spec"
- exit(4) if err == $stderr
- end
- end
- end
- end
-end
diff --git a/test/lib/spec/runner/reporter.rb b/test/lib/spec/runner/reporter.rb
deleted file mode 100644
index e4fb1cb0e..000000000
--- a/test/lib/spec/runner/reporter.rb
+++ /dev/null
@@ -1,105 +0,0 @@
-module Spec
- module Runner
- class Reporter
-
- def initialize(formatter, backtrace_tweaker)
- @formatter = formatter
- @backtrace_tweaker = backtrace_tweaker
- clear!
- end
-
- def add_context(name)
- #TODO - @context_names.empty? tells the formatter whether this is the first context or not - that's a little slippery
- @formatter.add_context(name, @context_names.empty?)
- @context_names << name
- end
-
- def spec_started(name)
- @spec_names << name
- @formatter.spec_started(name)
- end
-
- def spec_finished(name, error=nil, failure_location=nil)
- if error.nil?
- spec_passed(name)
- else
- @backtrace_tweaker.tweak_backtrace(error, failure_location)
- spec_failed(name, Failure.new(@context_names.last, name, error))
- end
- end
-
- def start(number_of_specs)
- clear!
- @start_time = Time.new
- @formatter.start(number_of_specs)
- end
-
- def end
- @end_time = Time.new
- end
-
- # Dumps the summary and returns the total number of failures
- def dump
- @formatter.start_dump
- dump_failures
- @formatter.dump_summary(duration, @spec_names.length, @failures.length)
- @failures.length
- end
-
- private
-
- def clear!
- @context_names = []
- @failures = []
- @spec_names = []
- @start_time = nil
- @end_time = nil
- end
-
- def dump_failures
- return if @failures.empty?
- @failures.inject(1) do |index, failure|
- @formatter.dump_failure(index, failure)
- index + 1
- end
- end
-
- def duration
- return @end_time - @start_time unless (@end_time.nil? or @start_time.nil?)
- return "0.0"
- end
-
- def spec_passed(name)
- @formatter.spec_passed(name)
- end
-
- def spec_failed(name, failure)
- @failures << failure
- @formatter.spec_failed(name, @failures.length, failure)
- end
-
- class Failure
- attr_reader :exception
-
- def initialize(context_name, spec_name, exception)
- @context_name = context_name
- @spec_name = spec_name
- @exception = exception
- end
-
- def header
- if expectation_not_met?
- "'#{@context_name} #{@spec_name}' FAILED"
- else
- "#{@exception.class.name} in '#{@context_name} #{@spec_name}'"
- end
- end
-
- def expectation_not_met?
- @exception.is_a?(Spec::Expectations::ExpectationNotMetError)
- end
-
- end
- end
- end
-end
diff --git a/test/lib/spec/runner/spec_matcher.rb b/test/lib/spec/runner/spec_matcher.rb
deleted file mode 100755
index 687fdaa00..000000000
--- a/test/lib/spec/runner/spec_matcher.rb
+++ /dev/null
@@ -1,25 +0,0 @@
-module Spec
- module Runner
- class SpecMatcher
-
- attr_writer :spec_desc
- def initialize(context_desc, spec_desc=nil)
- @context_desc = context_desc
- @spec_desc = spec_desc
- end
-
- def matches?(desc)
- desc =~ /(^#{context_regexp} #{spec_regexp}$|^#{context_regexp}$|^#{spec_regexp}$)/
- end
-
- private
- def context_regexp
- Regexp.escape(@context_desc)
- end
-
- def spec_regexp
- Regexp.escape(@spec_desc)
- end
- end
- end
-end
diff --git a/test/lib/spec/runner/spec_parser.rb b/test/lib/spec/runner/spec_parser.rb
deleted file mode 100644
index 2cb8518fc..000000000
--- a/test/lib/spec/runner/spec_parser.rb
+++ /dev/null
@@ -1,41 +0,0 @@
-module Spec
- module Runner
- # Parses a spec file and finds the nearest spec for a given line number.
- class SpecParser
- def spec_name_for(io, line_number)
- source = io.read
- context = context_at_line(source, line_number)
- spec = spec_at_line(source, line_number)
- if context && spec
- "#{context} #{spec}"
- elsif context
- context
- else
- nil
- end
- end
-
- protected
-
- def context_at_line(source, line_number)
- find_above(source, line_number, /^\s*context\s+['|"](.*)['|"]/)
- end
-
- def spec_at_line(source, line_number)
- find_above(source, line_number, /^\s*specify\s+['|"](.*)['|"]/)
- end
-
- def find_above(source, line_number, pattern)
- lines_above_reversed(source, line_number).each do |line|
- return $1 if line =~ pattern
- end
- nil
- end
-
- def lines_above_reversed(source, line_number)
- lines = source.split("\n")
- lines[0...line_number].reverse
- end
- end
- end
-end \ No newline at end of file
diff --git a/test/lib/spec/runner/specification.rb b/test/lib/spec/runner/specification.rb
deleted file mode 100644
index de8d750fd..000000000
--- a/test/lib/spec/runner/specification.rb
+++ /dev/null
@@ -1,114 +0,0 @@
-module Spec
- module Runner
- class Specification
-
- class << self
- attr_accessor :current, :generated_description
- protected :current=
-
- callback_events :before_setup, :after_teardown
- end
-
- attr_reader :spec_block
- callback_events :before_setup, :after_teardown
-
- def initialize(name, opts={}, &spec_block)
- @from = caller(0)[3]
- @description = name
- @options = opts
- @spec_block = spec_block
- @description_generated_callback = lambda { |desc| @generated_description = desc }
- end
-
- def run(reporter, setup_block, teardown_block, dry_run, execution_context)
- reporter.spec_started(name) if reporter
- return reporter.spec_finished(name) if dry_run
-
- errors = []
- begin
- set_current
- setup_ok = setup_spec(execution_context, errors, &setup_block)
- spec_ok = execute_spec(execution_context, errors) if setup_ok
- teardown_ok = teardown_spec(execution_context, errors, &teardown_block)
- ensure
- clear_current
- end
-
- SpecShouldRaiseHandler.new(@from, @options).handle(errors)
- reporter.spec_finished(name, errors.first, failure_location(setup_ok, spec_ok, teardown_ok)) if reporter
- end
-
- def matches?(matcher, description)
- matcher.spec_desc = name
- matcher.matches?(description)
- end
-
- private
- def name
- @description == :__generate_description ? generated_description : @description
- end
-
- def generated_description
- @generated_description || "NAME NOT GENERATED"
- end
-
- def setup_spec(execution_context, errors, &setup_block)
- notify_before_setup(errors)
- execution_context.instance_eval(&setup_block) if setup_block
- return errors.empty?
- rescue => e
- errors << e
- return false
- end
-
- def execute_spec(execution_context, errors)
- begin
- execution_context.instance_eval(&spec_block)
- return true
- rescue Exception => e
- errors << e
- return false
- end
- end
-
- def teardown_spec(execution_context, errors, &teardown_block)
- execution_context.instance_eval(&teardown_block) if teardown_block
- notify_after_teardown(errors)
- return errors.empty?
- rescue => e
- errors << e
- return false
- end
-
- def notify_before_setup(errors)
- notify_class_callbacks(:before_setup, self, &append_errors(errors))
- notify_callbacks(:before_setup, self, &append_errors(errors))
- end
-
- def notify_after_teardown(errors)
- notify_callbacks(:after_teardown, self, &append_errors(errors))
- notify_class_callbacks(:after_teardown, self, &append_errors(errors))
- end
-
- def append_errors(errors)
- proc {|error| errors << error}
- end
-
- def set_current
- Spec::Matchers.description_generated(&@description_generated_callback)
- self.class.send(:current=, self)
- end
-
- def clear_current
- Spec::Matchers.unregister_callback(:description_generated, @description_generated_callback)
- self.class.send(:current=, nil)
- end
-
- def failure_location(setup_ok, spec_ok, teardown_ok)
- return 'setup' unless setup_ok
- return name unless spec_ok
- return 'teardown' unless teardown_ok
- end
- end
- end
-end
diff --git a/test/lib/spec/translator.rb b/test/lib/spec/translator.rb
deleted file mode 100644
index 970c8ca00..000000000
--- a/test/lib/spec/translator.rb
+++ /dev/null
@@ -1,87 +0,0 @@
-require 'fileutils'
-
-module Spec
- class Translator
- def translate_dir(from, to)
- from = File.expand_path(from)
- to = File.expand_path(to)
- if File.directory?(from)
- FileUtils.mkdir_p(to) unless File.directory?(to)
- Dir["#{from}/*"].each do |sub_from|
- path = sub_from[from.length+1..-1]
- sub_to = File.join(to, path)
- translate_dir(sub_from, sub_to)
- end
- else
- translate_file(from, to)
- end
- end
-
- def translate_file(from, to)
- translation = ""
- File.open(from) do |io|
- io.each_line do |line|
- translation << translate(line)
- end
- end
- File.open(to, "w") do |io|
- io.write(translation)
- end
- end
-
- def translate(line)
- return line if line =~ /(should_not|should)_receive/
-
- if line =~ /(.*\.)(should_not|should)(?:_be)(?!_)(.*)/m
- pre = $1
- should = $2
- post = $3
- be_or_equal = post =~ /(<|>)/ ? "be" : "equal"
-
- return "#{pre}#{should} #{be_or_equal}#{post}"
- end
-
- if line =~ /(.*\.)(should_not|should)_(?!not)(.*)/m
- pre = $1
- should = $2
- post = $3
-
- post.gsub!(/^raise/, 'raise_error')
- post.gsub!(/^throw/, 'throw_symbol')
-
- unless standard_matcher?(post)
- post = "be_#{post}"
- end
-
- line = "#{pre}#{should} #{post}"
- end
-
- line
- end
-
- def standard_matcher?(matcher)
- patterns = [
- /^be/,
- /^be_close/,
- /^eql/,
- /^equal/,
- /^has/,
- /^have/,
- /^change/,
- /^include/,
- /^match/,
- /^raise_error/,
- /^respond_to/,
- /^satisfy/,
- /^throw_symbol/,
- # Extra ones that we use in spec_helper
- /^pass/,
- /^fail/,
- /^fail_with/,
- ]
- matched = patterns.detect{ |p| matcher =~ p }
- !matched.nil?
- end
-
- end
-end \ No newline at end of file
diff --git a/test/lib/spec/version.rb b/test/lib/spec/version.rb
deleted file mode 100644
index e692a87ee..000000000
--- a/test/lib/spec/version.rb
+++ /dev/null
@@ -1,30 +0,0 @@
-module Spec
- module VERSION
- def self.build_tag
- tag = "REL_" + [MAJOR, MINOR, TINY].join('_')
- if defined?(RELEASE_CANDIDATE)
- tag << "_" << RELEASE_CANDIDATE
- end
- tag
- end
-
- unless defined? MAJOR
- MAJOR = 0
- MINOR = 8
- TINY = 2
- # RELEASE_CANDIDATE = "RC1"
-
- # RANDOM_TOKEN: 0.375509844656552
- REV = "$LastChangedRevision: 2283$".match(/LastChangedRevision: (\d+)/)[1]
-
- STRING = [MAJOR, MINOR, TINY].join('.')
- FULL_VERSION = "#{STRING} (r#{REV})"
- TAG = build_tag
-
- NAME = "RSpec"
- URL = "http://rspec.rubyforge.org/"
-
- DESCRIPTION = "#{NAME}-#{FULL_VERSION} - BDD for Ruby\n#{URL}"
- end
- end
-end
diff --git a/test/network/client/client.rb b/test/network/client/client.rb
index 14c90f2a9..93c63d637 100755
--- a/test/network/client/client.rb
+++ b/test/network/client/client.rb
@@ -164,15 +164,13 @@ class TestClient < Test::Unit::TestCase
# Fake that it's local, so it creates the class file
client.local = false
+ # We can't guarantee class ordering
+ client.expects(:setclasses).with do |array|
+ array.length == 2 and array.include?("yaytest") and array.include?("bootest")
+ end
assert_nothing_raised {
client.getconfig
}
-
- assert(FileTest.exists?(Puppet[:classfile]), "Class file does not exist")
-
- classes = File.read(Puppet[:classfile]).split("\n")
-
- assert_equal(%w{bootest yaytest}, classes.sort)
end
def test_client_loading
diff --git a/test/network/client/master.rb b/test/network/client/master.rb
index 78a1a0a11..a29254d16 100755
--- a/test/network/client/master.rb
+++ b/test/network/client/master.rb
@@ -45,37 +45,30 @@ class TestMasterClient < Test::Unit::TestCase
@master = Puppet::Network::Client.master
end
- def mkmaster(file = nil)
- master = nil
-
- file ||= mktestmanifest()
+ def mkmaster(options = {})
+ options[:UseNodes] = false
+ options[:Local] = true
+ unless options[:Code]
+ options[:Manifest] ||= mktestmanifest
+ end
# create our master
- assert_nothing_raised() {
- # this is the default server setup
- master = Puppet::Network::Handler.master.new(
- :Manifest => file,
- :UseNodes => false,
- :Local => true
- )
- }
+ # this is the default server setup
+ master = Puppet::Network::Handler.master.new(options)
return master
end
def mkclient(master = nil)
master ||= mkmaster()
- client = nil
- assert_nothing_raised() {
- client = Puppet::Network::Client.master.new(
- :Master => master
- )
- }
+ client = Puppet::Network::Client.master.new(
+ :Master => master
+ )
return client
end
def mk_fake_client
server = Puppet::Network::Handler.master.new :Code => ""
- master = Puppet::Network::Client.master.new :Server => server, :Local => true
+ master = Puppet::Network::Client.master.new :Master => server, :Local => true
# Now create some objects
objects = FakeComponent.new
@@ -183,7 +176,7 @@ class TestMasterClient < Test::Unit::TestCase
FileUtils.mkdir_p(Puppet[:statedir])
manifest = mktestmanifest
- master = mkmaster(manifest)
+ master = mkmaster(:Manifest => manifest)
client = mkclient(master)
@@ -448,7 +441,7 @@ end
manifest = tempfile()
File.open(manifest, "w") { |f| f.puts "file { '#{file}': content => yay }" }
- driver = mkmaster(manifest)
+ driver = mkmaster(:Manifest => manifest)
driver.local = false
master = mkclient(driver)
@@ -682,6 +675,16 @@ end
end
end
+ def test_environment_is_added_to_facts
+ facts = Puppet::Network::Client::Master.facts
+ assert_equal(facts["environment"], Puppet[:environment], "Did not add environment to client facts")
+
+ # Now set it to a real value
+ Puppet[:environment] = "something"
+ facts = Puppet::Network::Client::Master.facts
+ assert_equal(facts["environment"], Puppet[:environment], "Did not add environment to client facts")
+ end
+
# This is partially to fix #532, but also to save on memory.
def test_remove_objects_after_every_run
client = mkclient
@@ -709,6 +712,21 @@ end
client.run
end
end
-end
-# $Id$
+ # #800 -- we cannot fix this using the current design.
+ def disabled_test_invalid_relationships_do_not_get_cached
+ # Make a master with an invalid relationship
+ master = mkmaster :Code => "notify { one: require => File[yaytest] }"
+ master.local = false # so it gets cached
+ client = mkclient(master)
+ client.local = false
+
+ client.getconfig
+ # Doesn't throw an exception, but definitely fails.
+ client.apply
+
+ # Make sure the config is not cached.
+ config = Puppet.config[:localconfig] + ".yaml"
+ assert(! File.exists?(config), "Cached an invalid configuration")
+ end
+end
diff --git a/test/network/handler/configuration.rb b/test/network/handler/configuration.rb
new file mode 100755
index 000000000..072fdc053
--- /dev/null
+++ b/test/network/handler/configuration.rb
@@ -0,0 +1,191 @@
+#!/usr/bin/env ruby
+
+$:.unshift("../../lib") if __FILE__ =~ /\.rb$/
+
+require 'puppettest'
+require 'puppet/network/handler/configuration'
+
+class TestHandlerConfiguration < Test::Unit::TestCase
+ include PuppetTest
+
+ Config = Puppet::Network::Handler.handler(:configuration)
+
+ # Check all of the setup stuff.
+ def test_initialize
+ config = nil
+ assert_nothing_raised("Could not create local config") do
+ config = Config.new(:Local => true)
+ end
+
+ assert(config.local?, "Config is not considered local after being started that way")
+ end
+
+ # Make sure we create the node handler when necessary.
+ def test_node_handler
+ config = Config.new
+ handler = nil
+ assert_nothing_raised("Could not create node handler") do
+ handler = config.send(:node_handler)
+ end
+ assert_instance_of(Puppet::Network::Handler.handler(:node), handler, "Did not create node handler")
+
+ # Now make sure we get the same object back again
+ assert_equal(handler.object_id, config.send(:node_handler).object_id, "Did not cache node handler")
+ end
+
+ # Test creation/returning of the interpreter
+ def test_interpreter
+ config = Config.new
+
+ # First test the defaults
+ args = {}
+ config.instance_variable_set("@options", args)
+ config.expects(:create_interpreter).with(args).returns(:interp)
+ assert_equal(:interp, config.send(:interpreter), "Did not return the interpreter")
+
+ # Now run it again and make sure we get the same thing
+ assert_equal(:interp, config.send(:interpreter), "Did not cache the interpreter")
+ end
+
+ def test_create_interpreter
+ config = Config.new(:Local => false)
+ args = {}
+
+ # Try it first with defaults.
+ Puppet::Parser::Interpreter.expects(:new).with(:Local => config.local?).returns(:interp)
+ assert_equal(:interp, config.send(:create_interpreter, args), "Did not return the interpreter")
+
+ # Now reset it and make sure a specified manifest passes through
+ file = tempfile
+ args[:Manifest] = file
+ Puppet::Parser::Interpreter.expects(:new).with(:Local => config.local?, :Manifest => file).returns(:interp)
+ assert_equal(:interp, config.send(:create_interpreter, args), "Did not return the interpreter")
+
+ # And make sure the code does, too
+ args.delete(:Manifest)
+ args[:Code] = "yay"
+ Puppet::Parser::Interpreter.expects(:new).with(:Local => config.local?, :Code => "yay").returns(:interp)
+ assert_equal(:interp, config.send(:create_interpreter, args), "Did not return the interpreter")
+ end
+
+ # Make sure node objects get appropriate data added to them.
+ def test_add_node_data
+ # First with no classes
+ config = Config.new
+
+ fakenode = Object.new
+ # Set the server facts to something
+ config.instance_variable_set("@server_facts", :facts)
+ fakenode.expects(:fact_merge).with(:facts)
+ config.send(:add_node_data, fakenode)
+
+ # Now try it with classes.
+ config.instance_variable_set("@options", {:Classes => %w{a b}})
+ list = []
+ fakenode = Object.new
+ fakenode.expects(:fact_merge).with(:facts)
+ fakenode.expects(:classes).returns(list).times(2)
+ config.send(:add_node_data, fakenode)
+ assert_equal(%w{a b}, list, "Did not add classes to node")
+ end
+
+ def test_compile
+ config = Config.new
+
+ # First do a local
+ node = mock 'node'
+ node.stubs(:name).returns(:mynode)
+ node.stubs(:environment).returns(:myenv)
+
+ interp = mock 'interpreter'
+ interp.stubs(:environment)
+ interp.expects(:compile).with(node).returns(:config)
+ config.expects(:interpreter).returns(interp)
+
+ Puppet.expects(:notice) # The log message from benchmarking
+
+ assert_equal(:config, config.send(:compile, node), "Did not return config")
+
+ # Now try it non-local
+ node = mock 'node'
+ node.stubs(:name).returns(:mynode)
+ node.stubs(:environment).returns(:myenv)
+
+ interp = mock 'interpreter'
+ interp.stubs(:environment)
+ interp.expects(:compile).with(node).returns(:config)
+
+ config = Config.new(:Local => true)
+ config.expects(:interpreter).returns(interp)
+
+ assert_equal(:config, config.send(:compile, node), "Did not return config")
+ end
+
+ def test_set_server_facts
+ config = Config.new
+ assert_nothing_raised("Could not call :set_server_facts") do
+ config.send(:set_server_facts)
+ end
+ facts = config.instance_variable_get("@server_facts")
+ %w{servername serverversion serverip}.each do |fact|
+ assert(facts.include?(fact), "Config did not set %s fact" % fact)
+ end
+ end
+
+ def test_translate
+ # First do a local config
+ config = Config.new(:Local => true)
+ assert_equal(:plain, config.send(:translate, :plain), "Attempted to translate local config")
+
+ # Now a non-local
+ config = Config.new(:Local => false)
+ obj = Object.new
+ yamld = Object.new
+ obj.expects(:to_yaml).with(:UseBlock => true).returns(yamld)
+ CGI.expects(:escape).with(yamld).returns(:translated)
+ assert_equal(:translated, config.send(:translate, obj), "Did not return translated config")
+ end
+
+ # Check that we're storing the node freshness into the rails db. Hackilicious.
+ def test_update_node_check
+ # This is stupid.
+ config = Config.new
+ node = Object.new
+ node.expects(:name).returns(:hostname)
+ now = Object.new
+ Time.expects(:now).returns(now)
+ host = Object.new
+ host.expects(:last_freshcheck=).with(now)
+ host.expects(:save)
+
+ # Only test the case where rails is there
+ Puppet[:storeconfigs] = true
+ Puppet.features.expects(:rails?).returns(true)
+ Puppet::Rails.expects(:connect)
+ Puppet::Rails::Host.expects(:find_or_create_by_name).with(:hostname).returns(host)
+
+ config.send(:update_node_check, node)
+ end
+
+ def test_version
+ # First try the case where we can't look up the node
+ config = Config.new
+ handler = Object.new
+ handler.expects(:details).with(:client).returns(false)
+ config.expects(:node_handler).returns(handler)
+ interp = Object.new
+ assert_instance_of(Bignum, config.version(:client), "Did not return configuration version")
+
+ # And then when we find the node.
+ config = Config.new
+ node = Object.new
+ handler = Object.new
+ handler.expects(:details).with(:client).returns(node)
+ config.expects(:update_node_check).with(node)
+ config.expects(:node_handler).returns(handler)
+ interp = Object.new
+ interp.expects(:configuration_version).returns(:version)
+ config.expects(:interpreter).returns(interp)
+ assert_equal(:version, config.version(:client), "Did not return configuration version")
+ end
+end
diff --git a/test/network/handler/master.rb b/test/network/handler/master.rb
index 08e17373b..a976726ef 100755
--- a/test/network/handler/master.rb
+++ b/test/network/handler/master.rb
@@ -8,56 +8,6 @@ require 'puppet/network/handler/master'
class TestMaster < Test::Unit::TestCase
include PuppetTest::ServerTest
- # run through all of the existing test files and make sure everything
- # works
- def test_files
- count = 0
- textfiles { |file|
- Puppet.debug("parsing %s" % file)
- client = nil
- master = nil
-
- # create our master
- assert_nothing_raised() {
- # this is the default server setup
- master = Puppet::Network::Handler.master.new(
- :Manifest => file,
- :UseNodes => false,
- :Local => true
- )
- }
-
- # and our client
- assert_nothing_raised() {
- client = Puppet::Network::Client.master.new(
- :Master => master
- )
- }
-
- # pull our configuration a few times
- assert_nothing_raised() {
- client.getconfig
- stopservices
- Puppet::Type.allclear
- }
- assert_nothing_raised() {
- client.getconfig
- stopservices
- Puppet::Type.allclear
- }
- assert_nothing_raised() {
- client.getconfig
- stopservices
- Puppet::Type.allclear
- }
- # only test three files; that's plenty
- if count > 3
- break
- end
- count += 1
- }
- end
-
def test_defaultmanifest
textfiles { |file|
Puppet[:manifest] = file
@@ -130,7 +80,7 @@ class TestMaster < Test::Unit::TestCase
assert(client.fresh?(facts), "Client is not up to date")
# Cache this value for later
- parse1 = master.freshness
+ parse1 = master.freshness("mynode")
# Verify the config got applied
assert(FileTest.exists?(@createdfile),
@@ -145,7 +95,7 @@ class TestMaster < Test::Unit::TestCase
# Verify that the master doesn't immediately reparse the file; we
# want to wait through the timeout
- assert_equal(parse1, master.freshness, "Master did not wait through timeout")
+ assert_equal(parse1, master.freshness("mynode"), "Master did not wait through timeout")
assert(client.fresh?(facts), "Client is not up to date")
# Then eliminate it
@@ -153,7 +103,7 @@ class TestMaster < Test::Unit::TestCase
# Now make sure the master does reparse
#Puppet.notice "%s vs %s" % [parse1, master.freshness]
- assert(parse1 != master.freshness, "Master did not reparse file")
+ assert(parse1 != master.freshness("mynode"), "Master did not reparse file")
assert(! client.fresh?(facts), "Client is incorrectly up to date")
# Retrieve and apply the new config
@@ -166,138 +116,39 @@ class TestMaster < Test::Unit::TestCase
assert(FileTest.exists?(file2), "Second file %s does not exist" % file2)
end
- def test_addfacts
- master = nil
- file = mktestmanifest()
- # create our master
- assert_nothing_raised() {
- # this is the default server setup
- master = Puppet::Network::Handler.master.new(
- :Manifest => file,
- :UseNodes => false,
- :Local => true
- )
- }
-
- facts = {}
-
- assert_nothing_raised {
- master.addfacts(facts)
- }
-
- %w{serverversion servername serverip}.each do |fact|
- assert(facts.include?(fact), "Fact %s was not set" % fact)
- end
- end
-
- # Make sure we're using the hostname as configured with :node_name
- def test_hostname_in_getconfig
- master = nil
- file = tempfile()
- #@createdfile = File.join(tmpdir(), self.class.to_s + "manifesttesting" +
- # "_" + @method_name)
- file_cert = tempfile()
- file_fact = tempfile()
-
- certname = "y4yn3ss"
- factname = Facter.value("hostname")
-
- File.open(file, "w") { |f|
- f.puts %{
- node #{certname} { file { "#{file_cert}": ensure => file, mode => 755 } }
- node #{factname} { file { "#{file_fact}": ensure => file, mode => 755 } }
-}
- }
- # create our master
- assert_nothing_raised() {
- # this is the default server setup
- master = Puppet::Network::Handler.master.new(
- :Manifest => file,
- :UseNodes => true,
- :Local => true
- )
- }
-
- result = nil
-
- # Use the hostname from facter
- Puppet[:node_name] = 'facter'
- assert_nothing_raised {
- result = master.getconfig({"hostname" => factname}, "yaml", certname, "127.0.0.1")
- }
-
- result = result.flatten
-
- assert(result.find { |obj| obj.name == file_fact },
- "Could not find correct file")
- assert(!result.find { |obj| obj.name == file_cert },
- "Found incorrect file")
-
- # Use the hostname from the cert
- Puppet[:node_name] = 'cert'
- assert_nothing_raised {
- result = master.getconfig({"hostname" => factname}, "yaml", certname, "127.0.0.1")
- }
-
- result = result.flatten
-
- assert(!result.find { |obj| obj.name == file_fact },
- "Could not find correct file")
- assert(result.find { |obj| obj.name == file_cert },
- "Found incorrect file")
- end
-
# Make sure we're correctly doing clientname manipulations.
# Testing to make sure we always get a hostname and IP address.
def test_clientname
- master = nil
- file = tempfile()
-
- File.open(file, "w") { |f|
- f.puts %{
- node yay { file { "/something": ensure => file, mode => 755 } }
-}
- }
# create our master
- assert_nothing_raised() {
- # this is the default server setup
- master = Puppet::Network::Handler.master.new(
- :Manifest => file,
- :UseNodes => true,
- :Local => true
- )
- }
+ master = Puppet::Network::Handler.master.new(
+ :Manifest => tempfile,
+ :UseNodes => true,
+ :Local => true
+ )
- Puppet[:node_name] = "cert"
- # First act like we're local
- fakename = nil
- fakeip = nil
- name = ip = nil
- facts = Facter.to_hash
- assert_nothing_raised do
- name, ip = master.clientname(fakename, fakeip, facts)
- end
-
- assert(facts["hostname"], "Removed hostname fact")
- assert(facts["ipaddress"], "Removed ipaddress fact")
-
- assert_equal(facts["hostname"], name)
- assert_equal(facts["ipaddress"], ip)
-
- # Now set them to something real, and make sure we get them back
- fakename = "yayness"
- fakeip = "192.168.0.1"
- facts = Facter.to_hash
- assert_nothing_raised do
- name, ip = master.clientname(fakename, fakeip, facts)
- end
-
- assert(facts["hostname"], "Removed hostname fact")
- assert(facts["ipaddress"], "Removed ipaddress fact")
+ # First check that 'cert' works
+ Puppet[:node_name] = "cert"
- assert_equal(fakename, name)
- assert_equal(fakeip, ip)
+ # Make sure we get the fact data back when nothing is set
+ facts = {"hostname" => "fact_hostname", "ipaddress" => "fact_ip"}
+ certname = "cert_hostname"
+ certip = "cert_ip"
+
+ resname, resip = master.send(:clientname, nil, nil, facts)
+ assert_equal(facts["hostname"], resname, "Did not use fact hostname when no certname was present")
+ assert_equal(facts["ipaddress"], resip, "Did not use fact ip when no certname was present")
+
+ # Now try it with the cert stuff present
+ resname, resip = master.send(:clientname, certname, certip, facts)
+ assert_equal(certname, resname, "Did not use cert hostname when certname was present")
+ assert_equal(certip, resip, "Did not use cert ip when certname was present")
+
+ # And reset the node_name stuff and make sure we use it.
+ Puppet[:node_name] = :facter
+ resname, resip = master.send(:clientname, certname, certip, facts)
+ assert_equal(facts["hostname"], resname, "Did not use fact hostname when nodename was set to facter")
+ assert_equal(facts["ipaddress"], resip, "Did not use fact ip when nodename was set to facter")
end
end
diff --git a/test/network/handler/node.rb b/test/network/handler/node.rb
index 9fad3f765..6b8ab9290 100755
--- a/test/network/handler/node.rb
+++ b/test/network/handler/node.rb
@@ -12,7 +12,7 @@ require 'puppet/network/handler/node'
module NodeTesting
include PuppetTest
Node = Puppet::Network::Handler::Node
- SimpleNode = Puppet::Network::Handler::Node::SimpleNode
+ SimpleNode = Puppet::Node
def mk_node_mapper
# First, make sure our nodesearch command works as we expect
@@ -66,7 +66,7 @@ module NodeTesting
end
end
-class TestNodeInterface < Test::Unit::TestCase
+class TestNodeHandler < Test::Unit::TestCase
include NodeTesting
def setup
@@ -193,6 +193,9 @@ class TestNodeInterface < Test::Unit::TestCase
# Make sure its source is set
node.expects(:source=).with(handler.source)
+ # And that the names are retained
+ node.expects(:names=).with(%w{a b c})
+
# And make sure we actually get it back
handler.expects(:nodesearch).with("c").returns(node)
@@ -315,70 +318,30 @@ class TestNodeInterface < Test::Unit::TestCase
end
assert_equal(%w{yay foo}, result, "Did not get classes back")
end
-end
-
-class TestSimpleNode < Test::Unit::TestCase
- include NodeTesting
-
- # Make sure we get all the defaults correctly.
- def test_simplenode_initialize
- node = nil
- assert_nothing_raised("could not create a node without classes or parameters") do
- node = SimpleNode.new("testing")
- end
- assert_equal("testing", node.name, "Did not set name correctly")
- assert_equal({}, node.parameters, "Node parameters did not default correctly")
- assert_equal([], node.classes, "Node classes did not default correctly")
-
- # Now test it with values for both
- params = {"a" => "b"}
- classes = %w{one two}
- assert_nothing_raised("could not create a node with classes and parameters") do
- node = SimpleNode.new("testing", :parameters => params, :classes => classes)
- end
- assert_equal("testing", node.name, "Did not set name correctly")
- assert_equal(params, node.parameters, "Node parameters did not get set correctly")
- assert_equal(classes, node.classes, "Node classes did not get set correctly")
-
- # And make sure a single class gets turned into an array
- assert_nothing_raised("could not create a node with a class as a string") do
- node = SimpleNode.new("testing", :classes => "test")
- end
- assert_equal(%w{test}, node.classes, "A node class string was not converted to an array")
- # Make sure we get environments
- assert_nothing_raised("could not create a node with an environment") do
- node = SimpleNode.new("testing", :environment => "test")
- end
- assert_equal("test", node.environment, "Environment was not set")
+ # We reuse the filetimeout for the node caching timeout.
+ def test_node_caching
+ handler = Node.new
- # Now make sure we get the default env
- Puppet[:environment] = "prod"
- assert_nothing_raised("could not create a node with no environment") do
- node = SimpleNode.new("testing")
+ node = Object.new
+ node.metaclass.instance_eval do
+ attr_accessor :time, :name
end
- assert_equal("prod", node.environment, "Did not get default environment")
+ node.time = Time.now
+ node.name = "yay"
- # But that it stays nil if there's no default env set
- Puppet[:environment] = ""
- assert_nothing_raised("could not create a node with no environment and no default env") do
- node = SimpleNode.new("testing")
+ # Make sure caching works normally
+ assert_nothing_raised("Could not cache node") do
+ handler.send(:cache, node)
end
- assert_nil(node.environment, "Got a default env when none was set")
-
- end
+ assert_equal(node.object_id, handler.send(:cached?, "yay").object_id, "Did not get node back from the cache")
- # Verify that the node source wins over facter.
- def test_fact_merge
- node = SimpleNode.new("yay", :parameters => {"a" => "one", "b" => "two"})
+ # And that it's returned if we ask for it, instead of creating a new node.
+ assert_equal(node.object_id, handler.details("yay").object_id, "Did not use cached node")
- assert_nothing_raised("Could not merge parameters") do
- node.fact_merge("b" => "three", "c" => "yay")
- end
- params = node.parameters
- assert_equal("one", params["a"], "Lost nodesource parameters in parameter merge")
- assert_equal("two", params["b"], "Overrode nodesource parameters in parameter merge")
- assert_equal("yay", params["c"], "Did not get facts in parameter merge")
+ # Now set the node's time to be a long time ago
+ node.time = Time.now - 50000
+ assert(! handler.send(:cached?, "yay"), "Timed-out node was returned from cache")
end
end
@@ -568,7 +531,7 @@ class LdapNodeTest < PuppetTest::TestCase
end
def ldaphost(name)
- node = NodeDef.new(:name => name)
+ node = Puppet::Node.new(name)
parent = nil
found = false
@ldap.search( "ou=hosts, dc=madstop, dc=com", 2,
diff --git a/test/network/handler/resource.rb b/test/network/handler/resource.rb
index 589d22d83..18f52dbd6 100755
--- a/test/network/handler/resource.rb
+++ b/test/network/handler/resource.rb
@@ -236,7 +236,7 @@ class TestResourceServer < Test::Unit::TestCase
def test_apply
server = nil
assert_nothing_raised do
- server = Puppet::Network::Handler.resource.new()
+ server = Puppet::Network::Handler.resource.new(:Local => false)
end
file = tempfile()
diff --git a/test/network/server/webrick.rb b/test/network/server/webrick.rb
index 69f23f3c2..a9448fe6c 100755
--- a/test/network/server/webrick.rb
+++ b/test/network/server/webrick.rb
@@ -86,10 +86,11 @@ class TestWebrickServer < Test::Unit::TestCase
kill_and_wait(serverpid, server.pidfile)
serverpid, server = mk_status_server
- client = mk_status_client
- # This time the client should be denied
- assert_raise(Puppet::Network::XMLRPCClientError) {
- client.status
+ # This time the client should be denied. With keep-alive,
+ # the client starts its connection immediately, thus throwing
+ # the error.
+ assert_raise(OpenSSL::SSL::SSLError) {
+ client = Puppet::Network::Client.status.new(:Server => "localhost", :Port => @@port)
}
end
diff --git a/test/network/xmlrpc/processor.rb b/test/network/xmlrpc/processor.rb
index 101d268b2..6808d0100 100755
--- a/test/network/xmlrpc/processor.rb
+++ b/test/network/xmlrpc/processor.rb
@@ -64,7 +64,7 @@ class TestXMLRPCProcessor < Test::Unit::TestCase
request.expects(:handler=).with("myhandler")
request.expects(:method=).with("mymethod")
- @processor.expects(:verify).times(2)
+ @processor.stubs(:verify)
@processor.expects(:handle).with(request.call,
"params", request.name, request.ip)
diff --git a/test/puppet/defaults.rb b/test/puppet/defaults.rb
index 19db3fe05..383153e45 100755
--- a/test/puppet/defaults.rb
+++ b/test/puppet/defaults.rb
@@ -38,32 +38,6 @@ class TestPuppetDefaults < Test::Unit::TestCase
}
end
- if __FILE__ == $0
- def disabled_testContained
- confdir = Regexp.new(Puppet[:confdir])
- vardir = Regexp.new(Puppet[:vardir])
- [@@dirs,@@files].flatten.each { |param|
- value = Puppet[param]
-
- unless value =~ confdir or value =~ vardir
- assert_nothing_raised { raise "%s is in wrong dir: %s" %
- [param,value] }
- end
- }
- end
- end
-
- def testArgumentTypes
- assert_raise(ArgumentError) { Puppet[["string"]] }
- assert_raise(ArgumentError) { Puppet[[:symbol]] }
- end
-
- def testFailOnBogusArgs
- [0, "ashoweklj", ";"].each { |param|
- assert_raise(ArgumentError, "No error on %s" % param) { Puppet[param] }
- }
- end
-
# we don't want user defaults in /, or root defaults in ~
def testDefaultsInCorrectRoots
notval = nil
diff --git a/test/puppet/modules.rb b/test/puppet/modules.rb
deleted file mode 100755
index 15976c64a..000000000
--- a/test/puppet/modules.rb
+++ /dev/null
@@ -1,59 +0,0 @@
-#!/usr/bin/env ruby
-
-$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
-
-require 'puppettest'
-
-class TestModules < Test::Unit::TestCase
- include PuppetTest
-
- def setup
- super
- @varmods = File::join(Puppet[:vardir], "modules")
- FileUtils::mkdir_p(@varmods)
- end
-
- def test_modulepath
- Puppet[:modulepath] = "$vardir/modules:/no/such/path/anywhere:.::"
- assert_equal([ @varmods ], Puppet::Module.modulepath)
- end
-
- def test_find
- assert_nil(Puppet::Module::find("/tmp"))
-
- file = "testmod/something"
- assert_nil(Puppet::Module::find(file))
-
- path = File::join(@varmods, "testmod")
- FileUtils::mkdir_p(path)
-
- mod = Puppet::Module::find("testmod")
- assert_not_nil(mod)
- assert_equal("testmod", mod.name)
- assert_equal(path, mod.path)
-
- mod = Puppet::Module::find(file)
- assert_not_nil(mod)
- assert_equal("testmod", mod.name)
- assert_equal(path, mod.path)
- end
-
- def test_find_template
- templ = "testmod/templ.erb"
- assert_equal(File::join(Puppet[:templatedir], templ),
- Puppet::Module::find_template(templ))
-
- templ_path = File::join(@varmods, "testmod",
- Puppet::Module::TEMPLATES, "templ.erb")
- FileUtils::mkdir_p(File::dirname(templ_path))
- File::open(templ_path, "w") { |f| f.puts "Howdy" }
-
- assert_equal(templ_path, Puppet::Module::find_template(templ))
-
- mod = Puppet::Module::find(templ)
- assert_not_nil(mod)
- assert_equal(templ_path, mod.template(templ))
- end
-end
-
-# $Id$
diff --git a/test/rails/ast.rb b/test/rails/ast.rb
index b205aa0d5..fb6374401 100755
--- a/test/rails/ast.rb
+++ b/test/rails/ast.rb
@@ -20,7 +20,8 @@ class TestRailsAST < PuppetTest::TestCase
def test_exported_collexp
railsinit
Puppet[:storeconfigs] = true
- @interp, @scope, @source = mkclassframing
+
+ @scope = mkscope
# make a rails resource
railsresource "file", "/tmp/testing", :owner => "root", :group => "bin",
diff --git a/test/rails/collection.rb b/test/rails/collection.rb
index d878641be..56f71e635 100755
--- a/test/rails/collection.rb
+++ b/test/rails/collection.rb
@@ -22,19 +22,17 @@ class TestRailsCollection < PuppetTest::TestCase
def setup
super
Puppet[:trace] = false
- @interp, @scope, @source = mkclassframing
+ @scope = mkscope
+ @scope.compile.send(:evaluate_main)
end
def test_collect_exported
railsinit
- # Set a hostname
- @scope.host = Facter.value(:hostname)
-
# make an exported resource
exported = mkresource(:type => "file", :title => "/tmp/exported",
:exported => true, :params => {:owner => "root"})
- @scope.setresource exported
+ @scope.compile.store_resource @scope, exported
assert(exported.exported?, "Object was not marked exported")
assert(exported.virtual?, "Object was not marked virtual")
@@ -42,7 +40,7 @@ class TestRailsCollection < PuppetTest::TestCase
# And a non-exported
real = mkresource(:type => "file", :title => "/tmp/real",
:params => {:owner => "root"})
- @scope.setresource real
+ @scope.compile.store_resource @scope, real
# Now make a collector
coll = nil
@@ -51,10 +49,10 @@ class TestRailsCollection < PuppetTest::TestCase
end
# Set it in our scope
- @scope.newcollection(coll)
+ @scope.compile.add_collection(coll)
# Make sure it's in the collections
- assert_equal([coll], @scope.collections)
+ assert_equal([coll], @scope.compile.collections)
# And try to collect the virtual resources.
ret = nil
@@ -111,39 +109,40 @@ class TestRailsCollection < PuppetTest::TestCase
# Now try storing our crap
# Remark this as exported
exported.exported = true
- host = Puppet::Rails::Host.store(
- :resources => [exported],
- :facts => facts,
- :name => facts["hostname"]
- )
+ exported.scope.stubs(:tags).returns([])
+ node = mknode(facts["hostname"])
+ node.parameters = facts
+ host = Puppet::Rails::Host.store(node, [exported])
assert(host, "did not get rails host")
host.save
# And make sure it's in there
newres = host.resources.find_by_restype_and_title_and_exported("file", "/tmp/exported", true)
assert(newres, "Did not find resource in db")
- interp, scope, source = mkclassframing
- scope.host = "two"
+ assert(newres.exported?, "Resource was not exported")
+
+ # Make a new set with a different node name
+ node = mknode("other")
+ compile = Puppet::Parser::Compile.new(node, mkparser)
+ compile.send(:evaluate_main)
+ compile.topscope.source = mock("source")
+
+ # It's important that it's a different name, since same-name resources are ignored.
+ assert_equal("other", compile.node.name, "Did not get correct node name")
# Now make a collector
coll = nil
assert_nothing_raised do
- coll = Puppet::Parser::Collector.new(scope, "file", nil, nil, :exported)
+ coll = Puppet::Parser::Collector.new(compile.topscope, "file", nil, nil, :exported)
end
- # Set it in our scope
- scope.newcollection(coll)
-
- # Make sure it's in the collections
- assert_equal([coll], scope.collections)
-
# And try to collect the virtual resources.
ret = nil
- assert_nothing_raised do
+ assert_nothing_raised("Could not collect exported resources") do
ret = coll.collect_exported
end
- assert_equal(["/tmp/exported"], ret.collect { |f| f.title })
+ assert_equal(["/tmp/exported"], ret.collect { |f| f.title }, "Did not find resource in collction")
# Make sure we can evaluate the same collection multiple times and
# that later collections do nothing
@@ -166,8 +165,7 @@ class TestRailsCollection < PuppetTest::TestCase
# Now make a normal resource
normal = mkresource(:type => "file", :title => "/tmp/conflicttest",
:params => {:owner => "root"})
- @scope.setresource normal
- @scope.host = "otherhost"
+ @scope.compile.store_resource @scope, normal
# Now make a collector
coll = nil
@@ -186,15 +184,13 @@ class TestRailsCollection < PuppetTest::TestCase
railsinit
# Make our configuration
- host = Puppet::Rails::Host.new(:name => "myhost")
+ host = Puppet::Rails::Host.new(:name => @scope.host)
host.resources.build(:title => "/tmp/hosttest", :restype => "file",
:exported => true)
host.save
- @scope.host = "myhost"
-
# Now make a collector
coll = nil
assert_nothing_raised do
@@ -224,8 +220,6 @@ class TestRailsCollection < PuppetTest::TestCase
host.save
- @scope.host = "otherhost"
-
# Now make a collector
coll = nil
assert_nothing_raised do
diff --git a/test/rails/interpreter.rb b/test/rails/configuration.rb
index 0eba3f590..752ea5375 100755
--- a/test/rails/interpreter.rb
+++ b/test/rails/configuration.rb
@@ -3,7 +3,6 @@
$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
require 'puppettest'
-require 'puppet/parser/interpreter'
require 'puppet/parser/parser'
require 'puppet/network/client'
require 'puppet/rails'
@@ -13,49 +12,39 @@ require 'puppettest/servertest'
require 'puppettest/railstesting'
-class InterpreterRailsTests < PuppetTest::TestCase
+class ConfigurationRailsTests < PuppetTest::TestCase
include PuppetTest
include PuppetTest::ServerTest
include PuppetTest::ParserTesting
include PuppetTest::ResourceTesting
include PuppetTest::RailsTesting
AST = Puppet::Parser::AST
- NodeDef = Puppet::Parser::Interpreter::NodeDef
confine "No rails support" => Puppet.features.rails?
# We need to make sure finished objects are stored in the db.
def test_finish_before_store
railsinit
- interp = mkinterp
+ compile = mkcompile
+ compile.ast_nodes = true
+ parser = compile.parser
- node = interp.newnode ["myhost"], :code => AST::ASTArray.new(:children => [
+ node = parser.newnode [compile.node.name], :code => AST::ASTArray.new(:children => [
resourcedef("file", "/tmp/yay", :group => "root"),
defaultobj("file", :owner => "root")
])
- interp.newclass "myclass", :code => AST::ASTArray.new(:children => [
- ])
-
- interp.newclass "sub", :parent => "myclass",
- :code => AST::ASTArray.new(:children => [
- resourceoverride("file", "/tmp/yay", :owner => "root")
- ]
- )
-
# Now do the rails crap
Puppet[:storeconfigs] = true
- interp.evaluate("myhost", {})
-
- # And then retrieve the object from rails
- #res = Puppet::Rails::Resource.find_by_restype_and_title("file", "/tmp/yay", :include => {:param_values => :param_names})
- res = Puppet::Rails::Resource.find_by_restype_and_title("file", "/tmp/yay")
-
- assert(res, "Did not get resource from rails")
-
- params = res.parameters
-
- assert_equal(["root"], params["owner"], "Did not get correct value for owner param")
+ Puppet::Rails::Host.expects(:store).with do |node, resources|
+ if res = resources.find { |r| r.type == "file" and r.title == "/tmp/yay" }
+ assert_equal("root", res["owner"], "Did not set default on resource")
+ true
+ else
+ raise "Resource was not passed to store()"
+ end
+ end
+ compile.compile
end
def test_hoststorage
@@ -79,13 +68,15 @@ class InterpreterRailsTests < PuppetTest::TestCase
facts = {}
Facter.each { |fact, val| facts[fact] = val }
+ node = mknode(facts["hostname"])
+ node.parameters = facts
objects = nil
assert_nothing_raised {
- objects = interp.run(facts["hostname"], facts)
+ objects = interp.compile(node)
}
- obj = Puppet::Rails::Host.find_by_name(facts["hostname"])
+ obj = Puppet::Rails::Host.find_by_name(node.name)
assert(obj, "Could not find host object")
end
end
diff --git a/test/rails/host.rb b/test/rails/host.rb
index f3190c047..5ac2f763e 100755
--- a/test/rails/host.rb
+++ b/test/rails/host.rb
@@ -35,7 +35,7 @@ class TestRailsHost < PuppetTest::TestCase
end
def test_store
- @interp, @scope, @source = mkclassframing
+ @scope = mkscope
# First make some objects
resources = []
4.times { |i|
@@ -56,13 +56,10 @@ class TestRailsHost < PuppetTest::TestCase
# Now try storing our crap
host = nil
+ node = mknode(facts["hostname"])
+ node.parameters = facts
assert_nothing_raised {
- host = Puppet::Rails::Host.store(
- :resources => resources,
- :facts => facts,
- :name => facts["hostname"],
- :classes => ["one", "two::three", "four"]
- )
+ host = Puppet::Rails::Host.store(node, resources)
}
assert(host, "Did not create host")
@@ -110,7 +107,7 @@ class TestRailsHost < PuppetTest::TestCase
# Change a few resources
resources.find_all { |r| r.title =~ /file2/ }.each do |r|
- r.set("loglevel", "notice", r.source)
+ r.send(:set_parameter, "loglevel", "notice")
end
# And add a new resource
@@ -124,13 +121,10 @@ class TestRailsHost < PuppetTest::TestCase
facts["test1"] = "changedfact"
facts.delete("ipaddress")
host = nil
+ node = mknode(facts["hostname"])
+ node.parameters = facts
assert_nothing_raised {
- host = Puppet::Rails::Host.store(
- :resources => resources,
- :facts => facts,
- :name => facts["hostname"],
- :classes => ["one", "two::three", "four"]
- )
+ host = Puppet::Rails::Host.store(node, resources)
}
# Make sure it sets the last_compile time
@@ -162,7 +156,7 @@ class TestRailsHost < PuppetTest::TestCase
Puppet[:storeconfigs] = true
# this is the default server setup
- master = Puppet::Network::Handler.master.new(
+ master = Puppet::Network::Handler.configuration.new(
:Code => "",
:UseNodes => true,
:Local => true
@@ -172,7 +166,7 @@ class TestRailsHost < PuppetTest::TestCase
Puppet::Rails::Host.new(:name => "test", :ip => "192.168.0.3").save
assert_nothing_raised("Failed to update last_connect for unknown host") do
- master.freshness("created",'192.168.0.1')
+ master.version("created",'192.168.0.1')
end
# Make sure it created the host
@@ -180,12 +174,10 @@ class TestRailsHost < PuppetTest::TestCase
assert(created, "Freshness did not create host")
assert(created.last_freshcheck,
"Did not set last_freshcheck on created host")
- assert_equal("192.168.0.1", created.ip,
- "Did not set IP address on created host")
# Now check on the existing host
assert_nothing_raised("Failed to update last_connect for unknown host") do
- master.freshness("test",'192.168.0.2')
+ master.version("test",'192.168.0.2')
end
# Recreate it, so we're not using the cached object.
@@ -194,8 +186,6 @@ class TestRailsHost < PuppetTest::TestCase
# Make sure it created the host
assert(host.last_freshcheck,
"Did not set last_freshcheck on existing host")
- assert_equal("192.168.0.3", host.ip,
- "Overrode IP on found host")
end
end
diff --git a/test/rails/railsparameter.rb b/test/rails/railsparameter.rb
index 82d978bb4..89c81ad30 100755
--- a/test/rails/railsparameter.rb
+++ b/test/rails/railsparameter.rb
@@ -21,8 +21,8 @@ class TestRailsParameter < Test::Unit::TestCase
railsinit
# Now create a source
- interp = mkinterp
- source = interp.newclass "myclass"
+ parser = mkparser
+ source = parser.newclass "myclass"
host = Puppet::Rails::Host.new(:name => "myhost")
diff --git a/test/rails/railsresource.rb b/test/rails/railsresource.rb
index b8e5450b3..ca582b8b0 100755
--- a/test/rails/railsresource.rb
+++ b/test/rails/railsresource.rb
@@ -7,12 +7,14 @@ require 'puppet/rails'
require 'puppettest'
require 'puppettest/railstesting'
require 'puppettest/resourcetesting'
+require 'puppettest/parsertesting'
# Don't do any tests w/out this class
if Puppet.features.rails?
class TestRailsResource < Test::Unit::TestCase
include PuppetTest::RailsTesting
include PuppetTest::ResourceTesting
+ include PuppetTest::ParserTesting
def setup
super
@@ -55,7 +57,7 @@ class TestRailsResource < Test::Unit::TestCase
resource = mktest_resource
# We need a scope
- interp, scope, source = mkclassframing
+ scope = mkscope
# Find the new resource and include all it's parameters.
resource = Puppet::Rails::Resource.find_by_id(resource.id)
@@ -69,7 +71,7 @@ class TestRailsResource < Test::Unit::TestCase
assert_equal("root", res[:owner])
assert_equal("644", res[:mode])
assert_equal("/tmp/to_resource", res.title)
- assert_equal(source, res.source)
+ assert_equal(scope.source, res.source)
end
def test_parameters
@@ -111,7 +113,7 @@ class TestExportedResources < PuppetTest::TestCase
def setup
super
Puppet[:trace] = false
- @interp, @scope, @source = mkclassframing
+ @scope = mkscope
end
confine "Missing rails support" => Puppet.features.rails?
diff --git a/test/ral/manager/type.rb b/test/ral/manager/type.rb
index cbe969fb3..534c35759 100755
--- a/test/ral/manager/type.rb
+++ b/test/ral/manager/type.rb
@@ -758,7 +758,7 @@ class TestType < Test::Unit::TestCase
exec = mk.call(4, :parent => comp)
assert_equal("/server/Exec[exec4]", exec.path)
- comp = Puppet::Type.newcomponent :type => "whatever", :name => "main[top]"
+ comp = Puppet::Type.newcomponent :type => "whatever", :name => "class[main]"
exec = mk.call(5, :parent => comp)
assert_equal("//Exec[exec5]", exec.path)
@@ -815,6 +815,23 @@ class TestType < Test::Unit::TestCase
end
end
end
-end
-# $Id$
+ # #801 -- resources only checked in noop should be rescheduled immediately.
+ def test_reschedule_when_noop
+ Puppet::Type.type(:schedule).mkdefaultschedules
+ file = Puppet::Type.type(:file).create(:path => "/tmp/whatever", :mode => "755", :noop => true, :schedule => :daily, :ensure => :file)
+
+ assert(file.noop?, "File not considered in noop")
+ assert(file.scheduled?, "File is not considered scheduled")
+
+ file.evaluate
+
+ assert_nil(file.cached(:checked), "Stored a checked time when running in noop mode when there were changes")
+ file.cache(:checked, nil)
+
+ file.stubs(:propertychanges).returns([])
+
+ file.evaluate
+ assert_instance_of(Time, file.cached(:checked), "Did not store a checked time when running in noop mode when there were no changes")
+ end
+end
diff --git a/test/ral/types/cron.rb b/test/ral/types/cron.rb
index 9a3466821..7b2e770f0 100755
--- a/test/ral/types/cron.rb
+++ b/test/ral/types/cron.rb
@@ -3,7 +3,6 @@
$:.unshift("../../lib") if __FILE__ =~ /\.rb$/
require 'puppettest'
-require 'spec'
# Test cron job creation, modification, and destruction
diff --git a/test/tagging/tagging.rb b/test/tagging/tagging.rb
deleted file mode 100755
index afab3faa4..000000000
--- a/test/tagging/tagging.rb
+++ /dev/null
@@ -1,170 +0,0 @@
-#!/usr/bin/env ruby
-
-$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
-
-require 'puppet'
-require 'puppettest'
-require 'puppettest/parsertesting'
-require 'puppettest/resourcetesting'
-
-class TestTagging < Test::Unit::TestCase
- include PuppetTest
- include PuppetTest::ParserTesting
- include PuppetTest::ResourceTesting
-
- # Make sure the scopes are getting the right tags
- def test_scopetags
- scope = nil
- assert_nothing_raised {
- scope = mkscope
- scope.name = "yayness"
- scope.type = "solaris"
- }
-
- assert_nothing_raised {
- assert_equal(%w{solaris}, scope.tags, "Incorrect scope tags")
- }
- end
-
- # Test deeper tags, where a scope gets all of its parent scopes' tags
- def test_deepscopetags
- scope = nil
- assert_nothing_raised {
- scope = mkscope
- scope.name = "yayness"
- scope.type = "solaris"
- scope = scope.newscope
- scope.name = "booness"
- scope.type = "apache"
- }
-
- assert_nothing_raised {
- # Scopes put their own tags first
- assert_equal(%w{apache solaris}, scope.tags, "Incorrect scope tags")
- }
- end
-
- # Verify that the tags make their way to the objects
- def test_objecttags
- scope = nil
- assert_nothing_raised {
- scope = mkscope
- scope.name = "yayness"
- scope.type = "solaris"
- }
-
- resource = mkresource :type => "file", :title => "/tmp/testing",
- :params => {:owner => "root"}, :file => "/yay", :line => 1,
- :scope => scope
-
- assert_nothing_raised {
- scope.setresource(resource)
- }
-
- assert_nothing_raised {
- assert_equal(%w{solaris file}, resource.tags,
- "Incorrect tags")
- }
- end
-
- # Make sure that specifying tags results in only those objects getting
- # run.
- def test_tagspecs
- a = tempfile()
- b = tempfile()
-
- afile = Puppet.type(:file).create(
- :path => a,
- :ensure => :file
- )
- afile.tag("a")
-
- bfile = Puppet.type(:file).create(
- :path => b,
- :ensure => :file
- )
- bfile.tag(:b)
-
- # First, make sure they get created when no spec'ed tags
- assert_events([:file_created,:file_created], afile, bfile)
- assert(FileTest.exists?(a), "A did not get created")
- assert(FileTest.exists?(b), "B did not get created")
- File.unlink(a)
- File.unlink(b)
-
- # Set the tags to a
- assert_nothing_raised {
- Puppet[:tags] = "a"
- }
-
- assert_events([:file_created], afile, bfile)
- assert(FileTest.exists?(a), "A did not get created")
- assert(!FileTest.exists?(b), "B got created")
- File.unlink(a)
-
- # Set the tags to b
- assert_nothing_raised {
- Puppet[:tags] = "b"
- }
-
- assert_events([:file_created], afile, bfile)
- assert(!FileTest.exists?(a), "A got created")
- assert(FileTest.exists?(b), "B did not get created")
- File.unlink(b)
-
- # Set the tags to something else
- assert_nothing_raised {
- Puppet[:tags] = "c"
- }
-
- assert_events([], afile, bfile)
- assert(!FileTest.exists?(a), "A got created")
- assert(!FileTest.exists?(b), "B got created")
-
- # Now set both tags
- assert_nothing_raised {
- Puppet[:tags] = "b, a"
- }
-
- assert_events([:file_created, :file_created], afile, bfile)
- assert(FileTest.exists?(a), "A did not get created")
- assert(FileTest.exists?(b), "B did not get created")
- File.unlink(a)
-
- end
-
- def test_metaparamtag
- path = tempfile()
-
- start = %w{some tags}
- tags = %w{a list of tags}
-
- obj = nil
- assert_nothing_raised do
- obj = Puppet.type(:file).create(
- :path => path,
- :ensure => "file",
- :tag => start
- )
- end
-
-
- assert(obj, "Did not make object")
-
- start.each do |tag|
- assert(obj.tagged?(tag), "Object was not tagged with %s" % tag)
- end
-
- tags.each do |tag|
- assert_nothing_raised {
- obj[:tag] = tag
- }
- end
-
- tags.each do |tag|
- assert(obj.tagged?(tag), "Object was not tagged with %s" % tag)
- end
- end
-end
-
-# $Id$
diff --git a/test/util/autoload.rb b/test/util/autoload.rb
index 493fd7f60..bae6d37d4 100755
--- a/test/util/autoload.rb
+++ b/test/util/autoload.rb
@@ -50,11 +50,6 @@ TestAutoload.newthing(:#{name.to_s})
return rbdir, loader
end
- def teardown
- super
- Puppet::Util::Autoload.clear
- end
-
def test_load
dir, loader = mk_loader(:yayness)
@@ -84,36 +79,18 @@ TestAutoload.newthing(:#{name.to_s})
assert(self.class.thing?(:mything),
"Did not get loaded thing")
- # Now clear everything, and test loadall
- assert_nothing_raised {
- Puppet::Util::Autoload.clear
- }
-
self.class.clear
- assert_nothing_raised {
- loader.loadall
- }
-
[:mything, :othing].each do |thing|
+ loader.load(thing)
assert(loader.loaded?(thing), "#{thing.to_s} not considered loaded")
assert(loader.loaded?("%s.rb" % thing), "#{thing.to_s} not considered loaded with .rb")
assert(Puppet::Util::Autoload.loaded?("yayness/%s" % thing), "%s not considered loaded by the main class" % thing)
assert(Puppet::Util::Autoload.loaded?("yayness/%s.rb" % thing), "%s not considered loaded by the main class with .rb" % thing)
- loaded = Puppet::Util::Autoload.loaded?("yayness/%s.rb" % thing)
- assert_equal("%s/%s.rb" % [dir, thing], loaded[:file], "File path was not set correctly in loaded store")
- assert_equal(self.class, loaded[:autoloader], "Loader was not set correctly in loaded store")
-
assert(self.class.thing?(thing),
"Did not get loaded #{thing.to_s}")
end
-
- Puppet::Util::Autoload.clear
- [:mything, :othing].each do |thing|
- assert(! loader.loaded?(thing), "#{thing.to_s} considered loaded after clear")
- assert(! Puppet::Util::Autoload.loaded?("yayness/%s" % thing), "%s considered loaded by the main class after clear" % thing)
- end
end
# Make sure that autoload dynamically modifies $: with the libdir as
@@ -126,5 +103,3 @@ TestAutoload.newthing(:#{name.to_s})
assert(loader.send(:searchpath).include?(dir), "searchpath does not include the libdir")
end
end
-
-# $Id$
diff --git a/test/util/config.rb b/test/util/config.rb
index 9a1017058..f99ad54b4 100755
--- a/test/util/config.rb
+++ b/test/util/config.rb
@@ -59,6 +59,21 @@ class TestConfig < Test::Unit::TestCase
}
end
+ # #795 - when --config=relative, we want to fully expand file paths.
+ def test_relative_paths_when_to_transportable
+ config = mkconfig
+ config.setdefaults :yay, :transtest => ["/what/ever", "yo"]
+ file = config.element(:transtest)
+
+ # Now override it with a relative path name
+ config[:transtest] = "here"
+
+ should = File.join(Dir.getwd, "here")
+
+ object = file.to_transportable[0]
+ assert_equal(should, object.name, "Did not translate relative pathnames to full path names")
+ end
+
def test_to_manifest
set_configs
manifest = nil
@@ -74,11 +89,12 @@ class TestConfig < Test::Unit::TestCase
end
trans = nil
+ node = Puppet::Node.new("node")
assert_nothing_raised do
- trans = interp.evaluate(nil, {})
+ trans = interp.compile(node)
end
assert_nothing_raised("Could not instantiate objects") {
- trans.to_type
+ trans.extract.to_type
}
end
@@ -161,11 +177,11 @@ class TestConfig < Test::Unit::TestCase
assert(! @config[:booltest], "Booltest is not false")
- assert_raise(Puppet::Error) {
+ assert_raise(ArgumentError) {
@config[:booltest] = "yayness"
}
- assert_raise(Puppet::Error) {
+ assert_raise(ArgumentError) {
@config[:booltest] = "/some/file"
}
end
@@ -204,7 +220,7 @@ class TestConfig < Test::Unit::TestCase
def test_getset
initial = "an initial value"
- assert_raise(Puppet::Error) {
+ assert_raise(ArgumentError) {
@config[:yayness] = initial
}
@@ -225,7 +241,7 @@ class TestConfig < Test::Unit::TestCase
@config.clear
}
- assert_equal(default, @config[:yayness])
+ assert_equal(default, @config[:yayness], "'clear' did not remove old values")
assert_nothing_raised {
@config[:yayness] = "not default"
@@ -374,27 +390,26 @@ yay = /a/path
:shoe => ["puppet", "b"] # our default name
)
@config.handlearg("--cliparam", "changed")
- @config.expects(:parse_file).returns(result).times(2)
+ @config.stubs(:parse_file).returns(result)
# First do it with our name being 'puppet'
assert_nothing_raised("Could not handle parse results") do
@config.parse(tempfile)
end
- assert_logged(:warning, /unknown configuration parameter bad/, "Did not log invalid config param")
-
+ assert_equal(:puppet, @config.name, "Did not get correct name")
assert_equal("main", @config[:main], "Did not get main value")
assert_equal("puppet", @config[:other], "Did not get name value")
assert_equal("changed", @config[:cliparam], "CLI values were overridden by config")
# Now switch names and make sure the parsing switches, too.
@config.clear(true)
- @config[:name] = :puppetd
assert_nothing_raised("Could not handle parse results") do
@config.parse(tempfile)
end
- assert_logged(:warning, /unknown configuration parameter bad/, "Did not log invalid config param")
+ @config[:name] = :puppetd
+ assert_equal(:puppetd, @config.name, "Did not get correct name")
assert_equal("main", @config[:main], "Did not get main value")
assert_equal("puppetd", @config[:other], "Did not get name value")
assert_equal("changed", @config[:cliparam], "CLI values were overridden by config")
@@ -413,6 +428,7 @@ yay = /a/path
)
file = tempfile
+ count = 0
{
:pass => {
@@ -432,17 +448,19 @@ yay = /a/path
%{{owner => you}}
]
}.each do |type, list|
+ count += 1
list.each do |value|
if type == :pass
value, should = value[0], value[1]
end
+ path = "/other%s" % count
# Write our file out
File.open(file, "w") do |f|
- f.puts %{[main]\nfile = /other%s} % value
+ f.puts %{[main]\nfile = #{path}#{value}}
end
if type == :fail
- assert_raise(Puppet::Error, "Did not fail on %s" % value.inspect) do
+ assert_raise(ArgumentError, "Did not fail on %s" % value.inspect) do
@config.send(:parse_file, file)
end
else
@@ -451,6 +469,7 @@ yay = /a/path
result = @config.send(:parse_file, file)
end
assert_equal(should, result[:main][:_meta][:file], "Got incorrect return for %s" % value.inspect)
+ assert_equal(path, result[:main][:file], "Got incorrect value for %s" % value.inspect)
end
end
end
@@ -490,6 +509,7 @@ yay = /a/path
# Get the actual object, so we can verify metadata
file = @config.element(:file)
+ assert_equal("/other", @config[:file], "Did not get correct value")
assert_equal("you", file.owner, "Did not pass on user")
assert_equal("you", file.group, "Did not pass on group")
assert_equal("644", file.mode, "Did not pass on mode")
@@ -853,37 +873,6 @@ yay = /a/path
end
end
- def test_booleans_and_integers
- config = mkconfig
- config.setdefaults(:mysection,
- :booltest => [false, "yay"],
- :inttest => [14, "yay"]
- )
-
- file = tempfile()
-
- File.open(file, "w") do |f|
- f.puts %{
-[main]
-booltest = true
-inttest = 27
-}
- end
-
- assert_nothing_raised {
- config.parse(file)
- }
-
- assert_equal(true, config[:booltest], "Boolean was not converted")
- assert_equal(27, config[:inttest], "Integer was not converted")
-
- # Now make sure that they get converted through handlearg
- config.handlearg("--inttest", "true")
- assert_equal(true, config[:inttest], "Boolean was not converted")
- config.handlearg("--no-booltest", "false")
- assert_equal(false, config[:booltest], "Boolean was not converted")
- end
-
# Make sure that tags are ignored when configuring
def test_configs_ignore_tags
config = mkconfig
@@ -939,15 +928,10 @@ inttest = 27
["$server/yayness.conf", file]
].each do |ary|
value, type = ary
- elem = nil
assert_nothing_raised {
- elem = config.newelement(
- :name => value,
- :default => value,
- :desc => name.to_s,
- :section => :yayness
- )
+ config.setdefaults(:yayness, value => { :default => value, :desc => name.to_s})
}
+ elem = config.element(value)
assert_instance_of(type, elem,
"%s got created as wrong type" % value.inspect)
@@ -963,11 +947,10 @@ inttest = 27
config.setdefaults(:mysection, :clichange => ["clichange", "yay"])
config.setdefaults(:mysection, :filechange => ["filechange", "yay"])
- file = tempfile()
- # Set one parameter in the file
- File.open(file, "w") { |f|
- f.puts %{[main]\nfilechange = filevalue}
- }
+ config.stubs(:read_file).returns(%{[main]\nfilechange = filevalue\n})
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+
assert_nothing_raised {
config.parse(file)
}
@@ -979,16 +962,14 @@ inttest = 27
# And leave the other unset
assert_equal("default", config[:default])
- assert_equal("filevalue", config[:filechange])
+ assert_equal("filevalue", config[:filechange], "Did not get value from file")
assert_equal("clivalue", config[:clichange])
- # Now rewrite the file
- File.open(file, "w") { |f|
- f.puts %{[main]\nfilechange = newvalue}
- }
-
- cfile = config.file
- cfile.send("tstamp=".intern, Time.now - 50)
+ # Now reparse
+ config.stubs(:read_file).returns(%{[main]\nfilechange = newvalue\n})
+ file = mock 'file'
+ file.stubs(:changed?).returns(true)
+ config.parse(file)
# And check all of the values
assert_equal("default", config[:default])
@@ -1073,21 +1054,17 @@ inttest = 27
config = mkconfig()
testing = nil
- elem = nil
assert_nothing_raised do
- elem = config.newelement :default => "yay",
- :name => :blocktest,
- :desc => "boo",
- :section => :test,
- :hook => proc { |value| testing = value }
+ config.setdefaults :test, :blocktest => {:default => "yay", :desc => "boo", :hook => proc { |value| testing = value }}
end
+ elem = config.element(:blocktest)
assert_nothing_raised do
assert_equal("yay", elem.value)
end
assert_nothing_raised do
- elem.value = "yaytest"
+ config[:blocktest] = "yaytest"
end
assert_nothing_raised do
@@ -1096,7 +1073,7 @@ inttest = 27
assert_equal("yaytest", testing)
assert_nothing_raised do
- elem.value = "another"
+ config[:blocktest] = "another"
end
assert_nothing_raised do
@@ -1190,9 +1167,6 @@ inttest = 27
assert_nothing_raised("Unknown parameter threw an exception") do
config.parse(file)
end
-
- assert(@logs.detect { |l| l.message =~ /unknown configuration/ and l.level == :warning },
- "Did not generate warning message")
end
def test_multiple_interpolations
@@ -1218,46 +1192,6 @@ inttest = 27
"Did not interpolate curlied variables")
end
- # Discovered from #734
- def test_set_parameter_hash
- @config.setdefaults(:section,
- :unchanged => ["unval", "yay"],
- :normal => ["normalval", "yay"],
- :cliparam => ["clival", "yay"],
- :file => ["/my/file", "yay"]
- )
-
- # Set the cli param using the cli method
- @config.handlearg("--cliparam", "other")
-
- # Make sure missing params just throw warnings, not errors
- assert_nothing_raised("Could not call set_parameter_hash with an invalid option") do
- @config.send(:set_parameter_hash, :missing => "something")
- end
-
- # Make sure normal values get set
- assert_nothing_raised("Could not call set_parameter_hash with a normal value") do
- @config.send(:set_parameter_hash, :normal => "abnormal")
- end
- assert_equal("abnormal", @config[:normal], "Value did not get set")
-
- # Make sure cli-set values don't get overridden
- assert_nothing_raised("Could not call set_parameter_hash with an override of a cli value") do
- @config.send(:set_parameter_hash, :cliparam => "something else")
- end
- assert_equal("other", @config[:cliparam], "CLI value was overridden by config value")
-
- # Make sure the meta stuff works
- assert_nothing_raised("Could not call set_parameter_hash with meta info") do
- @config.send(:set_parameter_hash, :file => "/other/file", :_meta => {:file => {:mode => "0755"}})
- end
- assert_equal("/other/file", @config[:file], "value with meta info was overridden by config value")
- assert_equal("0755", @config.element(:file).mode, "Did not set mode from meta info")
-
- # And make sure other params are unchanged
- assert_equal("unval", @config[:unchanged], "Unchanged value has somehow changed")
- end
-
# Test to make sure that we can set and get a short name
def test_celement_short_name
element = nil