#!/usr/bin/env ruby
$:.unshift("../lib").unshift("../../lib") if __FILE__ =~ /\.rb$/
require 'puppet'
require 'puppet/parser/parser'
require 'puppettest'
class TestParser < Test::Unit::TestCase
include PuppetTest::ParserTesting
def setup
super
Puppet[:parseonly] = true
#@lexer = Puppet::Parser::Lexer.new()
end
def test_each_file
textfiles { |file|
parser = mkparser
Puppet.debug("parsing %s" % file) if __FILE__ == $0
assert_nothing_raised() {
parser.file = file
parser.parse
}
Puppet::Type.eachtype { |type|
type.each { |obj|
assert(obj.file, "File is not set on %s" % obj.ref)
assert(obj.name, "Name is not set on %s" % obj.ref)
assert(obj.line, "Line is not set on %s" % obj.ref)
}
}
Puppet::Type.allclear
}
end
def test_failers
failers { |file|
parser = mkparser
Puppet.debug("parsing failer %s" % file) if __FILE__ == $0
assert_raise(Puppet::ParseError) {
parser.file = file
ast = parser.parse
scope = mkscope :interp => parser.interp
ast.evaluate :scope => scope
}
Puppet::Type.allclear
}
end
def test_arrayrvalues
parser = mkparser
ret = nil
file = tempfile()
assert_nothing_raised {
parser.string = "file { \"#{file}\": mode => [755, 640] }"
}
assert_nothing_raised {
ret = parser.parse
}
end
def mkmanifest(file)
name = File.join(tmpdir, "file%s" % rand(100))
@@tmpfiles << name
File.open(file, "w") { |f|
f.puts "file { \"%s\": ensure => file, mode => 755 }\n" %
name
}
end
def test_importglobbing
basedir = File.join(tmpdir(), "importesting")
@@tmpfiles << basedir
Dir.mkdir(basedir)
subdir = "subdir"
Dir.mkdir(File.join(basedir, subdir))
manifest = File.join(basedir, "manifest")
File.open(manifest, "w") { |f|
f.puts "import \"%s/*\"" % subdir
}
4.times { |i|
path = File.join(basedir, subdir, "subfile%s" % i)
mkmanifest(path)
}
assert_nothing_raised("Could not parse multiple files") {
parser = mkparser
parser.file = manifest
parser.parse
}
end
def test_nonexistent_import
basedir = File.join(tmpdir(), "importesting")
@@tmpfiles << basedir
Dir.mkdir(basedir)
manifest = File.join(basedir, "manifest")
File.open(manifest, "w") do |f|
f.puts "import \" no such file \""
end
assert_raise(Puppet::ParseError) {
parser = mkparser
parser.file = manifest
parser.parse
}
end
def test_trailingcomma
path = tempfile()
str = %{file { "#{path}": ensure => file, }
}
parser = mkparser
parser.string = str
assert_nothing_raised("Could not parse trailing comma") {
parser.parse
}
end
def test_importedclasses
imported = tempfile()
importer = tempfile()
made = tempfile()
File.open(imported, "w") do |f|
f.puts %{class foo { file { "#{made}": ensure => file }}}
end
File.open(importer, "w") do |f|
f.puts %{import "#{imported}"\ninclude foo}
end
parser = mkparser
parser.file = importer
# Make sure it parses fine
assert_nothing_raised {
parser.parse
}
# Now make sure it actually does the work
assert_creates(importer, made)
end
# Make sure fully qualified and unqualified files can be imported
def test_fqfilesandlocalfiles
dir = tempfile()
Dir.mkdir(dir)
importer = File.join(dir, "site.pp")
fullfile = File.join(dir, "full.pp")
localfile = File.join(dir, "local.pp")
files = []
File.open(importer, "w") do |f|
f.puts %{import "#{fullfile}"\ninclude full\nimport "local.pp"\ninclude local}
end
fullmaker = tempfile()
files << fullmaker
File.open(fullfile, "w") do |f|
f.puts %{class full { file { "#{fullmaker}": ensure => file }}}
end
localmaker = tempfile()
files << localmaker
File.open(localfile, "w") do |f|
f.puts %{class local { file { "#{localmaker}": ensure => file }}}
end
parser = mkparser
parser.file = importer
# Make sure it parses
assert_nothing_raised {
parser.parse
}
# Now make sure it actually does the work
assert_creates(importer, *files)
end
# Make sure the parser adds '.pp' when necessary
def test_addingpp
dir = tempfile()
Dir.mkdir(dir)
importer = File.join(dir, "site.pp")
localfile = File.join(dir, "local.pp")
files = []
File.open(importer, "w") do |f|
f.puts %{import "local"\ninclude local}
end
file = tempfile()
files << file
File.open(localfile, "w") do |f|
f.puts %{class local { file { "#{file}": ensure => file }}}
end
parser = mkparser
parser.file = importer
assert_nothing_raised {
parser.parse
}
end
# Make sure that file importing changes file relative names.
def test_changingrelativenames
dir = tempfile()
Dir.mkdir(dir)
Dir.mkdir(File.join(dir, "subdir"))
top = File.join(dir, "site.pp")
subone = File.join(dir, "subdir/subone")
subtwo = File.join(dir, "subdir/subtwo")
files = []
file = tempfile()
files << file
File.open(subone + ".pp", "w") do |f|
f.puts %{class one { file { "#{file}": ensure => file }}}
end
otherfile = tempfile()
files << otherfile
File.open(subtwo + ".pp", "w") do |f|
f.puts %{import "subone"\n class two inherits one {
file { "#{otherfile}": ensure => file }
}}
end
File.open(top, "w") do |f|
f.puts %{import "subdir/subtwo"}
end
parser = mkparser
parser.file = top
assert_nothing_raised {
parser.parse
}
end
# Defaults are purely syntactical, so it doesn't make sense to be able to
# collect them.
def test_uncollectabledefaults
string = "@Port { protocols => tcp }"
assert_raise(Puppet::ParseError) {
mkparser.parse(string)
}
end
# Verify that we can parse collections
def test_collecting
text = "Port <| |>"
parser = mkparser
parser.string = text
ret = nil
assert_nothing_raised {
ret = parser.parse
}
assert_instance_of(AST::ASTArray, ret)
ret.each do |obj|
assert_instance_of(AST::Collection, obj)
end
end
def test_emptyfile
file = tempfile()
File.open(file, "w") do |f|
f.puts %{}
end
parser = mkparser
parser.file = file
assert_nothing_raised {
parser.parse
}
end
def test_multiple_nodes_named
file = tempfile()
other = tempfile()
File.open(file, "w") do |f|
f.puts %{
node nodeA, nodeB {
file { "#{other}": ensure => file }
}
}
end
parser = mkparser
parser.file = file
ast = nil
assert_nothing_raised {
ast = parser.parse
}
end
def test_emptyarrays
str = %{$var = []\n}
parser = mkparser
parser.string = str
# Make sure it parses fine
assert_nothing_raised {
parser.parse
}
end
# Make sure function names aren't reserved words.
def test_functionnamecollision
str = %{tag yayness
tag(rahness)
file { "/tmp/yayness":
tag => "rahness",
ensure => exists
}
}
parser = mkparser
parser.string = str
# Make sure it parses fine
assert_nothing_raised {
parser.parse
}
end
def test_metaparams_in_definition_prototypes
parser = mkparser
assert_raise(Puppet::ParseError) {
parser.parse %{define mydef($schedule) {}}
}
assert_nothing_raised {
parser.parse %{define adef($schedule = false) {}}
parser.parse %{define mydef($schedule = daily) {}}
}
end
def test_parsingif
parser = mkparser
exec = proc do |val|
%{exec { "/bin/echo #{val}": logoutput => true }}
end
str1 = %{if true { #{exec.call("true")} }}
ret = nil
assert_nothing_raised {
ret = parser.parse(str1)[0]
}
assert_instance_of(Puppet::Parser::AST::IfStatement, ret)
str2 = %{if true { #{exec.call("true")} } else { #{exec.call("false")} }}
assert_nothing_raised {
ret = parser.parse(str2)[0]
}
assert_instance_of(Puppet::Parser::AST::IfStatement, ret)
assert_instance_of(Puppet::Parser::AST::Else, ret.else)
end
def test_hostclass
parser = mkparser
interp = parser.interp
assert_nothing_raised {
parser.parse %{class myclass { class other {} }}
}
assert(interp.findclass("", "myclass"), "Could not find myclass")
assert(interp.findclass("", "myclass::other"), "Could not find myclass::other")
assert_nothing_raised {
parser.parse "class base {}
class container {
class deep::sub inherits base {}
}"
}
sub = interp.findclass("", "container::deep::sub")
assert(sub, "Could not find sub")
assert_equal("base", sub.parentclass.type)
# Now try it with a parent class being a fq class
assert_nothing_raised {
parser.parse "class container::one inherits container::deep::sub {}"
}
sub = interp.findclass("", "container::one")
assert(sub, "Could not find one")
assert_equal("sub", sub.parentclass.type)
# Finally, try including a qualified class
assert_nothing_raised("Could not include fully qualified class") {
parser.parse "include container::deep::sub"
}
end
def test_topnamespace
parser = mkparser
parser.interp.clear
# Make sure we put the top-level code into a class called "" in
# the "" namespace
assert_nothing_raised do
out = parser.parse ""
assert_nil(out)
assert_nil(parser.interp.findclass("", ""))
end
# Now try something a touch more complicated
parser.interp.clear
assert_nothing_raised do
out = parser.parse "Exec { path => '/usr/bin:/usr/sbin' }"
assert_instance_of(AST::ASTArray, out)
assert_equal("", parser.interp.findclass("", "").type)
assert_equal("", parser.interp.findclass("", "").namespace)
assert_equal(out.object_id, parser.interp.findclass("", "").code.object_id)
end
end
# Make sure virtual and exported resources work appropriately.
def test_virtualresources
tests = [:virtual]
if Puppet.features.rails?
Puppet[:storeconfigs] = true
tests << :exported
end
tests.each do |form|
parser = mkparser
if form == :virtual
at = "@"
else
at = "@@"
end
check = proc do |res, msg|
if res.is_a?(Puppet::Parser::Resource)
txt = res.ref
else
txt = res.class
end
# Real resources get marked virtual when exported
if form == :virtual or res.is_a?(Puppet::Parser::Resource)
assert(res.virtual, "#{msg} #{at}#{txt} is not virtual")
end
if form == :virtual
assert(! res.exported, "#{msg} #{at}#{txt} is exported")
else
assert(res.exported, "#{msg} #{at}#{txt} is not exported")
end
end
ret = nil
assert_nothing_raised do
ret = parser.parse("#{at}file { '/tmp/testing': owner => root }")
end
assert_equal("/tmp/testing", ret[0].title.value)
# We always get an astarray back, so...
assert_instance_of(AST::ResourceDef, ret[0])
check.call(ret[0], "simple resource")
# Now let's try it with multiple resources in the same spec
assert_nothing_raised do
ret = parser.parse("#{at}file { ['/tmp/1', '/tmp/2']: owner => root }")
end
assert_instance_of(AST::ASTArray, ret)
ret.each do |res|
assert_instance_of(AST::ResourceDef, res)
check.call(res, "multiresource")
end
# Now evaluate these
scope = mkscope
klass = scope.interp.newclass ""
scope.source = klass
assert_nothing_raised do
ret.evaluate :scope => scope
end
# Make sure we can find both of them
%w{/tmp/1 /tmp/2}.each do |title|
res = scope.findresource("File[#{title}]")
assert(res, "Could not find %s" % title)
check.call(res, "found multiresource")
end
end
end
def test_collections
tests = [:virtual]
if Puppet.features.rails?
Puppet[:storeconfigs] = true
tests << :exported
end
tests.each do |form|
parser = mkparser
if form == :virtual
arrow = "<||>"
else
arrow = "<<||>>"
end
check = proc do |coll|
assert_instance_of(AST::Collection, coll)
assert_equal(form, coll.form)
end
ret = nil
assert_nothing_raised do
ret = parser.parse("File #{arrow}")
end
check.call(ret[0])
end
end
def test_collectionexpressions
%w{== !=}.each do |oper|
str = "File <| title #{oper} '/tmp/testing' |>"
parser = mkparser
res = nil
assert_nothing_raised do
res = parser.parse(str)[0]
end
assert_instance_of(AST::Collection, res)
query = res.query
assert_instance_of(AST::CollExpr, query)
assert_equal(:virtual, query.form)
assert_equal("title", query.test1.value)
assert_equal("/tmp/testing", query.test2.value)
assert_equal(oper, query.oper)
end
end
def test_collectionstatements
%w{and or}.each do |joiner|
str = "File <| title == '/tmp/testing' #{joiner} owner == root |>"
parser = mkparser
res = nil
assert_nothing_raised do
res = parser.parse(str)[0]
end
assert_instance_of(AST::Collection, res)
query = res.query
assert_instance_of(AST::CollExpr, query)
assert_equal(joiner, query.oper)
assert_instance_of(AST::CollExpr, query.test1)
assert_instance_of(AST::CollExpr, query.test2)
end
end
def test_collectionstatements_with_parens
[
"(title == '/tmp/testing' and owner == root) or owner == wheel",
"(title == '/tmp/testing')"
].each do |test|
str = "File <| #{test} |>"
parser = mkparser
res = nil
assert_nothing_raised("Could not parse '#{test}'") do
res = parser.parse(str)[0]
end
assert_instance_of(AST::Collection, res)
query = res.query
assert_instance_of(AST::CollExpr, query)
#assert_equal(joiner, query.oper)
#assert_instance_of(AST::CollExpr, query.test1)
#assert_instance_of(AST::CollExpr, query.test2)
end
end
# We've had problems with files other than site.pp importing into main.
def test_importing_into_main
top = tempfile()
other = tempfile()
File.open(top, "w") do |f|
f.puts "import '#{other}'"
end
file = tempfile()
File.open(other, "w") do |f|
f.puts "file { '#{file}': ensure => present }"
end
interp = mkinterp :Manifest => top, :UseNodes => false
code = nil
assert_nothing_raised do
code = interp.run("hostname.domain.com", {}).flatten
end
assert(code.length == 1, "Did not get the file")
assert_instance_of(Puppet::TransObject, code[0])
end
def test_fully_qualified_definitions
parser = mkparser
interp = parser.interp
assert_nothing_raised("Could not parse fully-qualified definition") {
parser.parse %{define one::two { }}
}
assert(interp.finddefine("", "one::two"), "Could not find one::two with no namespace")
assert(interp.finddefine("one", "two"), "Could not find two in namespace one")
# Now try using the definition
assert_nothing_raised("Could not parse fully-qualified definition usage") {
parser.parse %{one::two { yayness: }}
}
end
# #524
def test_functions_with_no_arguments
parser = mkparser
assert_nothing_raised("Could not parse statement function with no args") {
parser.parse %{tag()}
}
assert_nothing_raised("Could not parse rvalue function with no args") {
parser.parse %{$testing = template()}
}
end
def test_module_import
basedir = File.join(tmpdir(), "module-import")
@@tmpfiles << basedir
Dir.mkdir(basedir)
modfiles = [ "init.pp", "mani1.pp", "mani2.pp",
"sub/smani1.pp", "sub/smani2.pp" ]
modpath = File.join(basedir, "modules")
Puppet[:modulepath] = modpath
modname = "amod"
manipath = File::join(modpath, modname, Puppet::Module::MANIFESTS)
FileUtils::mkdir_p(File::join(manipath, "sub"))
targets = []
modfiles.each do |fname|
target = File::join(basedir, File::basename(fname, '.pp'))
targets << target
txt = %[ file { '#{target}': content => "#{fname}" } ]
if fname == "init.pp"
txt = %[import 'mani1' \nimport '#{modname}/mani2'\nimport '#{modname}/sub/*.pp' ] + txt
end
File::open(File::join(manipath, fname), "w") do |f|
f.puts txt
end
end
manifest_texts = [ "import '#{modname}'",
"import '#{modname}/init'",
"import '#{modname}/init.pp'" ]
manifest = File.join(modpath, "manifest.pp")
manifest_texts.each do |txt|
File.open(manifest, "w") { |f| f.puts txt }
assert_nothing_raised {
parser = mkparser
parser.file = manifest
parser.parse
}
assert_creates(manifest, *targets)
end
end
# #544
def test_ignoreimports
parser = mkparser
assert(! Puppet[:ignoreimport], ":ignoreimport defaulted to true")
assert_raise(Puppet::ParseError, "Did not fail on missing import") do
parser.parse("import 'nosuchfile'")
end
assert_nothing_raised("could not set :ignoreimport") do
Puppet[:ignoreimport] = true
end
assert_nothing_raised("Parser did not follow :ignoreimports") do
parser.parse("import 'nosuchfile'")
end
end
def test_multiple_imports_on_one_line
one = tempfile
two = tempfile
base = tempfile
File.open(one, "w") { |f| f.puts "$var = value" }
File.open(two, "w") { |f| f.puts "$var = value" }
File.open(base, "w") { |f| f.puts "import '#{one}', '#{two}'" }
parser = mkparser
parser.file = base
# Importing is logged at debug time.
Puppet::Util::Log.level = :debug
assert_nothing_raised("Parser could not import multiple files at once") do
parser.parse
end
[one, two].each do |file|
assert(@logs.detect { |l| l.message =~ /importing '#{file}'/},
"did not import %s" % file)
end
end
def test_cannot_assign_qualified_variables
parser = mkparser
assert_raise(Puppet::ParseError, "successfully assigned a qualified variable") do
parser.parse("$one::two = yay")
end
end
# #588
def test_globbing_with_directories
dir = tempfile
Dir.mkdir(dir)
subdir = File.join(dir, "subdir")
Dir.mkdir(subdir)
file = File.join(dir, "file.pp")
maker = tempfile
File.open(file, "w") { |f| f.puts "file { '#{maker}': ensure => file }" }
parser = mkparser
assert_nothing_raised("Globbing failed when it matched a directory") do
parser.import("%s/*" % dir)
end
end
end
# $Id: parser.rb 2404 2007-04-20 16:40:47Z luke $
syntax highlighted by Code2HTML, v. 0.9.1