summaryrefslogtreecommitdiffstats
path: root/test/lib
diff options
context:
space:
mode:
authorerikh <erikh@980ebf18-57e1-0310-9a29-db15c13687c0>2006-09-15 04:00:38 +0000
committererikh <erikh@980ebf18-57e1-0310-9a29-db15c13687c0>2006-09-15 04:00:38 +0000
commit4cd37ade1574a326f9ce267af2edc61a78deea86 (patch)
tree1093cba18d4c7d7d4170c255db442f3dc0c774a7 /test/lib
parent4897995e4cbe4ec3e452de50404799cfc27ed90b (diff)
downloadpuppet-4cd37ade1574a326f9ce267af2edc61a78deea86.tar.gz
puppet-4cd37ade1574a326f9ce267af2edc61a78deea86.tar.xz
puppet-4cd37ade1574a326f9ce267af2edc61a78deea86.zip
Merged test framework into trunk - still not ready until tests are converted to use it.
git-svn-id: https://reductivelabs.com/svn/puppet/trunk@1601 980ebf18-57e1-0310-9a29-db15c13687c0
Diffstat (limited to 'test/lib')
-rw-r--r--test/lib/exetest.rb120
-rw-r--r--test/lib/fakes.rb99
-rw-r--r--test/lib/puppettest.rb1277
-rw-r--r--test/lib/scaffold.rb94
-rw-r--r--test/lib/servertest.rb68
-rw-r--r--test/lib/support.rb8
-rw-r--r--test/lib/support/assertions.rb67
-rw-r--r--test/lib/support/file.rb228
-rw-r--r--test/lib/support/helpers.rb60
-rw-r--r--test/lib/support/parser.rb344
-rw-r--r--test/lib/support/utils.rb146
11 files changed, 2511 insertions, 0 deletions
diff --git a/test/lib/exetest.rb b/test/lib/exetest.rb
new file mode 100644
index 000000000..8e1568851
--- /dev/null
+++ b/test/lib/exetest.rb
@@ -0,0 +1,120 @@
+require 'servertest'
+
+module ExeTest
+ include ServerTest
+
+ def setup
+ super
+ setbindir
+ setlibdir
+ end
+
+ def bindir
+ File.join($puppetbase, "bin")
+ end
+
+ def setbindir
+ unless ENV["PATH"] =~ /puppet/
+ ENV["PATH"] += ":" + bindir
+ end
+ end
+
+ def setlibdir
+ ENV["RUBYLIB"] = $:.find_all { |dir|
+ dir =~ /puppet/ or dir =~ /\.\./
+ }.join(":")
+ end
+
+ # Run a ruby command. This explicitly uses ruby to run stuff, since we
+ # don't necessarily know where our ruby binary is, dernit.
+ # Currently unused, because I couldn't get it to work.
+ def rundaemon(*cmd)
+ @ruby ||= %x{which ruby}.chomp
+ cmd = cmd.unshift(@ruby).join(" ")
+
+ out = nil
+ Dir.chdir(bindir()) {
+ out = %x{#{@ruby} #{cmd}}
+ }
+ return out
+ end
+
+ def startmasterd(args = "")
+ output = nil
+
+ manifest = mktestmanifest()
+ args += " --manifest %s" % manifest
+ args += " --confdir %s" % Puppet[:confdir]
+ args += " --vardir %s" % Puppet[:vardir]
+ args += " --masterport %s" % @@port
+ args += " --user %s" % Process.uid
+ args += " --group %s" % Process.gid
+ args += " --nonodes"
+ args += " --autosign true"
+
+ #if Puppet[:debug]
+ # args += " --debug"
+ #end
+
+ cmd = "puppetmasterd %s" % args
+
+
+ assert_nothing_raised {
+ output = %x{#{cmd}}.chomp
+ }
+ assert_equal("", output, "Puppetmasterd produced output %s" % output)
+ assert($? == 0, "Puppetmasterd exit status was %s" % $?)
+ sleep(1)
+
+ cleanup do
+ stopmasterd
+ sleep(1)
+ end
+
+ return manifest
+ end
+
+ def stopmasterd(running = true)
+ ps = Facter["ps"].value || "ps -ef"
+
+ pidfile = File.join(Puppet[:vardir], "run", "puppetmasterd.pid")
+
+ pid = nil
+ if FileTest.exists?(pidfile)
+ pid = File.read(pidfile).chomp.to_i
+ File.unlink(pidfile)
+ end
+
+ return unless running
+ if running or pid
+ runningpid = nil
+ %x{#{ps}}.chomp.split(/\n/).each { |line|
+ if line =~ /ruby.+puppetmasterd/
+ next if line =~ /\.rb/ # skip the test script itself
+ next if line =~ /^puppet/ # skip masters running as 'puppet'
+ ary = line.sub(/^\s+/, '').split(/\s+/)
+ pid = ary[1].to_i
+ end
+ }
+
+ end
+
+ # we default to mandating that it's running, but teardown
+ # doesn't require that
+ if pid
+ if pid == $$
+ raise Puppet::Error, "Tried to kill own pid"
+ end
+ begin
+ Process.kill(:INT, pid)
+ rescue
+ # ignore it
+ end
+ end
+ end
+
+ def teardown
+ stopmasterd(false)
+ super
+ end
+end
diff --git a/test/lib/fakes.rb b/test/lib/fakes.rb
new file mode 100644
index 000000000..d81c1fa5b
--- /dev/null
+++ b/test/lib/fakes.rb
@@ -0,0 +1,99 @@
+module PuppetTestFakes
+ # A baseclass for the faketypes.
+ class FakeModel < Hash
+ class << self
+ attr_accessor :name
+ @name = :fakemodel
+ end
+
+ def self.validstates
+ Puppet::Type.type(@name).validstates
+ end
+
+ def self.validstate?(name)
+ Puppet::Type.type(@name).validstate?(name)
+ end
+
+ def initialize(name)
+ self[:name] = name
+ end
+
+ def inspect
+ "%s(%s)" % [self.class.to_s.sub(/.+::/, ''), super()]
+ end
+
+ def name
+ self[:name]
+ end
+ end
+
+ class FakeProvider
+ attr_accessor :model
+ class << self
+ attr_accessor :name, :model, :methods
+ end
+
+ # A very low number, so these never show up as defaults via the standard
+ # algorithms.
+ def self.defaultnum
+ -50
+ end
+
+ # Set up methods to fake things
+ def self.apimethods(*ary)
+ @model.validstates.each do |state|
+ ary << state unless ary.include? state
+ end
+ attr_accessor(*ary)
+
+ @methods = ary
+ end
+
+ def self.initvars
+ @calls = Hash.new do |hash, key|
+ hash[key] = 0
+ end
+ end
+
+ def self.suitable?
+ true
+ end
+
+ def initialize(model)
+ @model = model
+ end
+ end
+
+ @@fakemodels = {}
+ @@fakeproviders = {}
+
+ def fakemodel(type, name, options = {})
+ type = type.intern if type.is_a? String
+ unless @@fakemodels.include? type
+ @@fakemodels[type] = Class.new(FakeModel)
+ @@fakemodels[type].name = type
+ end
+
+ obj = @@fakemodels[type].new(name)
+ obj[:name] = name
+ options.each do |name, val|
+ obj[name] = val
+ end
+ obj
+ end
+
+ module_function :fakemodel
+
+ def fakeprovider(type, model)
+ type = type.intern if type.is_a? String
+ unless @@fakeproviders.include? type
+ @@fakeproviders[type] = Class.new(FakeModel) do
+ @name = type
+ end
+ end
+
+ @@fakeproviders[type].new(model)
+ end
+
+ module_function :fakeprovider
+end
diff --git a/test/lib/puppettest.rb b/test/lib/puppettest.rb
new file mode 100644
index 000000000..e1af58e0a
--- /dev/null
+++ b/test/lib/puppettest.rb
@@ -0,0 +1,1277 @@
+libdir = File.join(File.dirname(__FILE__), '../lib')
+unless $:.include?(libdir)
+ $:.unshift libdir
+end
+
+require 'puppet'
+require 'test/unit'
+
+################################################################################
+# OLD CODE
+################################################################################
+module TestPuppet
+ include ObjectSpace
+
+ # A baseclass for the faketypes.
+ class FakeModel < Hash
+ class << self
+ attr_accessor :name
+ @name = :fakemodel
+ end
+
+ def self.validstates
+ Puppet::Type.type(@name).validstates
+ end
+
+ def self.validstate?(name)
+ Puppet::Type.type(@name).validstate?(name)
+ end
+
+ def initialize(name)
+ self[:name] = name
+ end
+
+ def inspect
+ "%s(%s)" % [self.class.to_s.sub(/.+::/, ''), super()]
+ end
+
+ def name
+ self[:name]
+ end
+ end
+
+ class FakeProvider
+ attr_accessor :model
+ class << self
+ attr_accessor :name, :model, :methods
+ end
+
+ # A very low number, so these never show up as defaults via the standard
+ # algorithms.
+ def self.defaultnum
+ -50
+ end
+
+ # Set up methods to fake things
+ def self.apimethods(*ary)
+ @model.validstates.each do |state|
+ ary << state unless ary.include? state
+ end
+ attr_accessor(*ary)
+
+ @methods = ary
+ end
+
+ def self.initvars
+ @calls = Hash.new do |hash, key|
+ hash[key] = 0
+ end
+ end
+
+ def self.suitable?
+ true
+ end
+
+ def initialize(model)
+ @model = model
+ end
+ end
+
+ @@fakemodels = {}
+ @@fakeproviders = {}
+
+ def fakemodel(type, name, options = {})
+ type = type.intern if type.is_a? String
+ unless @@fakemodels.include? type
+ @@fakemodels[type] = Class.new(FakeModel)
+ @@fakemodels[type].name = type
+ end
+
+ obj = @@fakemodels[type].new(name)
+ obj[:name] = name
+ options.each do |name, val|
+ obj[name] = val
+ end
+ obj
+ end
+
+ def fakeprovider(type, model)
+ type = type.intern if type.is_a? String
+ unless @@fakeproviders.include? type
+ @@fakeproviders[type] = Class.new(FakeModel) do
+ @name = type
+ end
+ end
+
+ @@fakeproviders[type].new(model)
+ end
+
+ def gcdebug(type)
+ Puppet.warning "%s: %s" % [type, ObjectSpace.each_object(type) { |o| }]
+ end
+
+ def newcomp(*ary)
+ name = nil
+ if ary[0].is_a?(String)
+ name = ary.shift
+ else
+ name = ary[0].title
+ end
+
+ comp = Puppet.type(:component).create(
+ :name => name
+ )
+ ary.each { |item|
+ comp.push item
+ }
+
+ return comp
+ end
+
+ def setup
+ @memoryatstart = Puppet::Util.memory
+ if defined? @@testcount
+ @@testcount += 1
+ else
+ @@testcount = 0
+ end
+
+ @configpath = File.join(tmpdir,
+ self.class.to_s + "configdir" + @@testcount.to_s + "/"
+ )
+
+ unless defined? $user and $group
+ $user = nonrootuser().uid.to_s
+ $group = nonrootgroup().gid.to_s
+ end
+ Puppet[:user] = $user
+ Puppet[:group] = $group
+
+ Puppet[:confdir] = @configpath
+ Puppet[:vardir] = @configpath
+
+ unless File.exists?(@configpath)
+ Dir.mkdir(@configpath)
+ end
+
+ @@tmpfiles = [@configpath, tmpdir()]
+ @@tmppids = []
+
+ @@cleaners = []
+
+ if $0 =~ /.+\.rb/ or Puppet[:debug]
+ Puppet::Log.newdestination :console
+ Puppet::Log.level = :debug
+ #$VERBOSE = 1
+ Puppet.info @method_name
+ else
+ Puppet::Log.close
+ Puppet::Log.newdestination tempfile()
+ Puppet[:httplog] = tempfile()
+ end
+
+ Puppet[:ignoreschedules] = true
+ end
+
+ def newobj(type, name, hash)
+ transport = Puppet::TransObject.new(name, "file")
+ transport[:path] = path
+ transport[:ensure] = "file"
+ assert_nothing_raised {
+ file = transport.to_type
+ }
+ end
+
+ def spin
+ # Just disable spin, unless we really need it
+ return
+# if Puppet[:debug]
+# return
+# end
+# @modes = %w{| / - \\}
+# unless defined? @mode
+# @mode = 0
+# end
+#
+# $stderr.print "%s" % @modes[@mode]
+# if @mode == @modes.length - 1
+# @mode = 0
+# else
+# @mode += 1
+# end
+# $stderr.flush
+ end
+
+ # stop any services that might be hanging around
+ def stopservices
+ if stype = Puppet::Type.type(:service)
+ stype.each { |service|
+ service[:ensure] = :stopped
+ service.evaluate
+ }
+ end
+ end
+
+ def cleanup(&block)
+ @@cleaners << block
+ end
+
+ def setme
+ # retrieve the user name
+ id = %x{id}.chomp
+ if id =~ /uid=\d+\(([^\)]+)\)/
+ @me = $1
+ else
+ puts id
+ end
+ unless defined? @me
+ raise "Could not retrieve user name; 'id' did not work"
+ end
+ end
+
+ def teardown
+ stopservices
+
+ @@cleaners.each { |cleaner| cleaner.call() }
+
+ @@tmpfiles.each { |file|
+ if FileTest.exists?(file)
+ system("chmod -R 755 %s" % file)
+ system("rm -rf %s" % file)
+ end
+ }
+ @@tmpfiles.clear
+
+ @@tmppids.each { |pid|
+ %x{kill -INT #{pid} 2>/dev/null}
+ }
+
+ @@tmppids.clear
+ Puppet::Type.allclear
+ Puppet::Storage.clear
+ Puppet::Rails.clear
+ Puppet.clear
+
+ @memoryatend = Puppet::Util.memory
+ diff = @memoryatend - @memoryatstart
+
+ if diff > 1000
+ Puppet.info "%s#%s memory growth (%s to %s): %s" %
+ [self.class, @method_name, @memoryatstart, @memoryatend, diff]
+ end
+
+ # reset all of the logs
+ Puppet::Log.close
+
+ # Just in case there are processes waiting to die...
+ Process.waitall
+ if File.stat("/dev/null").mode & 007777 != 0666
+ File.open("/tmp/nullfailure", "w") { |f|
+ f.puts self.class
+ }
+ exit(74)
+ end
+ end
+
+ def tempfile
+ if defined? @@tmpfilenum
+ @@tmpfilenum += 1
+ else
+ @@tmpfilenum = 1
+ end
+
+ f = File.join(self.tmpdir(), self.class.to_s + "_" + @method_name +
+ @@tmpfilenum.to_s)
+ @@tmpfiles << f
+ return f
+ end
+
+ def tstdir
+ tempfile()
+ end
+
+ def tmpdir
+ unless defined? @tmpdir and @tmpdir
+ @tmpdir = case Facter["operatingsystem"].value
+ when "Darwin": "/private/tmp"
+ when "SunOS": "/var/tmp"
+ else
+ "/tmp"
+ end
+
+
+ @tmpdir = File.join(@tmpdir, "puppettesting")
+
+ unless File.exists?(@tmpdir)
+ FileUtils.mkdir_p(@tmpdir)
+ File.chmod(01777, @tmpdir)
+ end
+ end
+ @tmpdir
+ end
+
+ def assert_rollback_events(events, trans, msg = nil)
+ run_events(:rollback, events, trans, msg)
+ end
+
+ def assert_events(events, *items)
+ trans = nil
+ comp = nil
+ msg = nil
+
+ unless events.is_a? Array
+ raise Puppet::DevError, "Incorrect call of assert_events"
+ end
+ if items[-1].is_a? String
+ msg = items.pop
+ end
+
+ remove_comp = false
+ # They either passed a comp or a list of items.
+ if items[0].is_a? Puppet.type(:component)
+ comp = items.shift
+ else
+ comp = newcomp(items[0].title, *items)
+ remove_comp = true
+ end
+ msg ||= comp.title
+ assert_nothing_raised("Component %s failed" % [msg]) {
+ trans = comp.evaluate
+ }
+
+ run_events(:evaluate, trans, events, msg)
+
+ if remove_comp
+ Puppet.type(:component).delete(comp)
+ end
+
+ return trans
+ end
+
+ # A simpler method that just applies what we have.
+ def assert_apply(*objects)
+ if objects[0].is_a?(Puppet.type(:component))
+ comp = objects.shift
+ unless objects.empty?
+ objects.each { |o| comp.push o }
+ end
+ else
+ comp = newcomp(*objects)
+ end
+ trans = nil
+
+ assert_nothing_raised("Failed to create transaction") {
+ trans = comp.evaluate
+ }
+
+ events = nil
+ assert_nothing_raised("Failed to evaluate transaction") {
+ events = trans.evaluate.collect { |e| e.event }
+ }
+ Puppet.type(:component).delete(comp)
+ events
+ end
+
+ def run_events(type, trans, events, msg)
+ case type
+ when :evaluate, :rollback: # things are hunky-dory
+ else
+ raise Puppet::DevError, "Incorrect run_events type"
+ end
+
+ method = type
+
+ newevents = nil
+ assert_nothing_raised("Transaction %s %s failed" % [type, msg]) {
+ newevents = trans.send(method).reject { |e| e.nil? }.collect { |e|
+ e.event
+ }
+ }
+
+ assert_equal(events, newevents, "Incorrect %s %s events" % [type, msg])
+
+ return trans
+ end
+
+ def nonrootuser
+ Etc.passwd { |user|
+ if user.uid != Process.uid and user.uid > 0
+ return user
+ end
+ }
+ end
+
+ def nonrootgroup
+ Etc.group { |group|
+ if group.gid != Process.gid and group.gid > 0
+ return group
+ end
+ }
+ end
+
+ # If there are any fake data files, retrieve them
+ def fakedata(dir)
+ ary = [$puppetbase, "test"]
+ ary += dir.split("/")
+ dir = File.join(ary)
+
+ unless FileTest.exists?(dir)
+ raise Puppet::DevError, "No fakedata dir %s" % dir
+ end
+ files = Dir.entries(dir).reject { |f| f =~ /^\./ }.collect { |f|
+ File.join(dir, f)
+ }
+
+ return files
+ end
+
+ def fakefile(name)
+ ary = [$puppetbase, "test"]
+ ary += name.split("/")
+ file = File.join(ary)
+ unless FileTest.exists?(file)
+ raise Puppet::DevError, "No fakedata file %s" % file
+ end
+ return file
+ end
+
+ # wrap how to retrieve the masked mode
+ def filemode(file)
+ File.stat(file).mode & 007777
+ end
+
+ def memory
+ Puppet::Util.memory
+ end
+end
+
+
+module ServerTest
+ include TestPuppet
+ def setup
+ super
+
+ if defined? @@port
+ @@port += 1
+ else
+ @@port = 20000
+ end
+ end
+
+ # create a simple manifest that just creates a file
+ def mktestmanifest
+ file = File.join(Puppet[:confdir], "%ssite.pp" % (self.class.to_s + "test"))
+ #@createdfile = File.join(tmpdir(), self.class.to_s + "manifesttesting" +
+ # "_" + @method_name)
+ @createdfile = tempfile()
+
+ File.open(file, "w") { |f|
+ f.puts "file { \"%s\": ensure => file, mode => 755 }\n" % @createdfile
+ }
+
+ @@tmpfiles << @createdfile
+ @@tmpfiles << file
+
+ return file
+ end
+
+ # create a server, forked into the background
+ def mkserver(handlers = nil)
+ # our default handlers
+ unless handlers
+ handlers = {
+ :CA => {}, # so that certs autogenerate
+ :Master => {
+ :Manifest => mktestmanifest(),
+ :UseNodes => false
+ },
+ }
+ end
+
+ # then create the actual server
+ server = nil
+ assert_nothing_raised {
+ server = Puppet::Server.new(
+ :Port => @@port,
+ :Handlers => handlers
+ )
+ }
+
+ # fork it
+ spid = fork {
+ trap(:INT) { server.shutdown }
+ server.start
+ }
+
+ # and store its pid for killing
+ @@tmppids << spid
+
+ # give the server a chance to do its thing
+ sleep 1
+ return spid
+ end
+
+end
+
+module ExeTest
+ include ServerTest
+
+ def setup
+ super
+ setbindir
+ setlibdir
+ end
+
+ def bindir
+ File.join($puppetbase, "bin")
+ end
+
+ def setbindir
+ unless ENV["PATH"] =~ /puppet/
+ ENV["PATH"] += ":" + bindir
+ end
+ end
+
+ def setlibdir
+ ENV["RUBYLIB"] = $:.find_all { |dir|
+ dir =~ /puppet/ or dir =~ /\.\./
+ }.join(":")
+ end
+
+ # Run a ruby command. This explicitly uses ruby to run stuff, since we
+ # don't necessarily know where our ruby binary is, dernit.
+ # Currently unused, because I couldn't get it to work.
+ def rundaemon(*cmd)
+ @ruby ||= %x{which ruby}.chomp
+ cmd = cmd.unshift(@ruby).join(" ")
+
+ out = nil
+ Dir.chdir(bindir()) {
+ out = %x{#{@ruby} #{cmd}}
+ }
+ return out
+ end
+
+ def startmasterd(args = "")
+ output = nil
+
+ manifest = mktestmanifest()
+ args += " --manifest %s" % manifest
+ args += " --confdir %s" % Puppet[:confdir]
+ args += " --vardir %s" % Puppet[:vardir]
+ args += " --masterport %s" % @@port
+ args += " --user %s" % Process.uid
+ args += " --group %s" % Process.gid
+ args += " --nonodes"
+ args += " --autosign true"
+
+ #if Puppet[:debug]
+ # args += " --debug"
+ #end
+
+ cmd = "puppetmasterd %s" % args
+
+
+ assert_nothing_raised {
+ output = %x{#{cmd}}.chomp
+ }
+ assert_equal("", output, "Puppetmasterd produced output %s" % output)
+ assert($? == 0, "Puppetmasterd exit status was %s" % $?)
+ sleep(1)
+
+ cleanup do
+ stopmasterd
+ sleep(1)
+ end
+
+ return manifest
+ end
+
+ def stopmasterd(running = true)
+ ps = Facter["ps"].value || "ps -ef"
+
+ pidfile = File.join(Puppet[:vardir], "run", "puppetmasterd.pid")
+
+ pid = nil
+ if FileTest.exists?(pidfile)
+ pid = File.read(pidfile).chomp.to_i
+ File.unlink(pidfile)
+ end
+
+ return unless running
+ if running or pid
+ runningpid = nil
+ %x{#{ps}}.chomp.split(/\n/).each { |line|
+ if line =~ /ruby.+puppetmasterd/
+ next if line =~ /\.rb/ # skip the test script itself
+ next if line =~ /^puppet/ # skip masters running as 'puppet'
+ ary = line.sub(/^\s+/, '').split(/\s+/)
+ pid = ary[1].to_i
+ end
+ }
+
+ end
+
+ # we default to mandating that it's running, but teardown
+ # doesn't require that
+ if pid
+ if pid == $$
+ raise Puppet::Error, "Tried to kill own pid"
+ end
+ begin
+ Process.kill(:INT, pid)
+ rescue
+ # ignore it
+ end
+ end
+ end
+
+ def teardown
+ stopmasterd(false)
+ super
+ end
+end
+
+module FileTesting
+ include TestPuppet
+ def cycle(comp)
+ trans = nil
+ assert_nothing_raised {
+ trans = comp.evaluate
+ }
+ assert_nothing_raised {
+ trans.evaluate
+ }
+ end
+
+ def randlist(list)
+ num = rand(4)
+ if num == 0
+ num = 1
+ end
+ set = []
+
+ ret = []
+ num.times { |index|
+ item = list[rand(list.length)]
+ if set.include?(item)
+ redo
+ end
+
+ ret.push item
+ }
+ return ret
+ end
+
+ def mkranddirsandfiles(dirs = nil,files = nil,depth = 3)
+ if depth < 0
+ return
+ end
+
+ unless dirs
+ dirs = %w{This Is A Set Of Directories}
+ end
+
+ unless files
+ files = %w{and this is a set of files}
+ end
+
+ tfiles = randlist(files)
+ tdirs = randlist(dirs)
+
+ tfiles.each { |file|
+ File.open(file, "w") { |of|
+ 4.times {
+ of.puts rand(100)
+ }
+ }
+ }
+
+ tdirs.each { |dir|
+ # it shouldn't already exist, but...
+ unless FileTest.exists?(dir)
+ Dir.mkdir(dir)
+ FileUtils.cd(dir) {
+ mkranddirsandfiles(dirs,files,depth - 1)
+ }
+ end
+ }
+ end
+
+ def file_list(dir)
+ list = nil
+ FileUtils.cd(dir) {
+ list = %x{find . 2>/dev/null}.chomp.split(/\n/)
+ }
+ return list
+ end
+
+ def assert_trees_equal(fromdir,todir)
+ assert(FileTest.directory?(fromdir))
+ assert(FileTest.directory?(todir))
+
+ # verify the file list is the same
+ fromlist = nil
+ FileUtils.cd(fromdir) {
+ fromlist = %x{find . 2>/dev/null}.chomp.split(/\n/).reject { |file|
+ ! FileTest.readable?(file)
+ }.sort
+ }
+ tolist = file_list(todir).sort
+
+ fromlist.sort.zip(tolist.sort).each { |a,b|
+ assert_equal(a, b,
+ "Fromfile %s with length %s does not match tofile %s with length %s" %
+ [a, fromlist.length, b, tolist.length])
+ }
+ #assert_equal(fromlist,tolist)
+
+ # and then do some verification that the files are actually set up
+ # the same
+ checked = 0
+ fromlist.each_with_index { |file,i|
+ fromfile = File.join(fromdir,file)
+ tofile = File.join(todir,file)
+ fromstat = File.stat(fromfile)
+ tostat = File.stat(tofile)
+ [:ftype,:gid,:mode,:uid].each { |method|
+ assert_equal(
+ fromstat.send(method),
+ tostat.send(method)
+ )
+
+ next if fromstat.ftype == "directory"
+ if checked < 10 and i % 3 == 0
+ from = File.open(fromfile) { |f| f.read }
+ to = File.open(tofile) { |f| f.read }
+
+ assert_equal(from,to)
+ checked += 1
+ end
+ }
+ }
+ end
+
+ def random_files(dir)
+ checked = 0
+ list = file_list(dir)
+ list.reverse.each_with_index { |file,i|
+ path = File.join(dir,file)
+ stat = File.stat(dir)
+ if checked < 10 and (i % 3) == 2
+ unless yield path
+ next
+ end
+ checked += 1
+ end
+ }
+ end
+
+ def delete_random_files(dir)
+ deleted = []
+ random_files(dir) { |file|
+ stat = File.stat(file)
+ begin
+ if stat.ftype == "directory"
+ false
+ else
+ deleted << file
+ File.unlink(file)
+ true
+ end
+ rescue => detail
+ # we probably won't be able to open our own secured files
+ puts detail
+ false
+ end
+ }
+
+ return deleted
+ end
+
+ def add_random_files(dir)
+ added = []
+ random_files(dir) { |file|
+ stat = File.stat(file)
+ begin
+ if stat.ftype == "directory"
+ name = File.join(file,"file" + rand(100).to_s)
+ File.open(name, "w") { |f|
+ f.puts rand(10)
+ }
+ added << name
+ else
+ false
+ end
+ rescue => detail
+ # we probably won't be able to open our own secured files
+ puts detail
+ false
+ end
+ }
+ return added
+ end
+
+ def modify_random_files(dir)
+ modded = []
+ random_files(dir) { |file|
+ stat = File.stat(file)
+ begin
+ if stat.ftype == "directory"
+ false
+ else
+ File.open(file, "w") { |f|
+ f.puts rand(10)
+ }
+ modded << name
+ true
+ end
+ rescue => detail
+ # we probably won't be able to open our own secured files
+ puts detail
+ false
+ end
+ }
+ return modded
+ end
+
+ def readonly_random_files(dir)
+ modded = []
+ random_files(dir) { |file|
+ stat = File.stat(file)
+ begin
+ if stat.ftype == "directory"
+ File.new(file).chmod(0111)
+ else
+ File.new(file).chmod(0000)
+ end
+ modded << file
+ rescue => detail
+ # we probably won't be able to open our own secured files
+ puts detail
+ false
+ end
+ }
+ return modded
+ end
+
+ def conffile
+ File.join($puppetbase,"examples/root/etc/configfile")
+ end
+end
+
+module ParserTesting
+ include TestPuppet
+ AST = Puppet::Parser::AST
+
+ def astarray(*args)
+ AST::ASTArray.new(
+ :children => args
+ )
+ end
+
+ def classobj(name, args = {})
+ args[:type] ||= nameobj(name)
+ args[:code] ||= AST::ASTArray.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :children => [
+ varobj("%svar" % name, "%svalue" % name),
+ fileobj("/%s" % name)
+ ]
+ )
+ assert_nothing_raised("Could not create class %s" % name) {
+ return AST::ClassDef.new(args)
+ }
+ end
+
+ def tagobj(*names)
+ args = {}
+ newnames = names.collect do |name|
+ if name.is_a? AST
+ name
+ else
+ nameobj(name)
+ end
+ end
+ args[:type] = astarray(*newnames)
+ assert_nothing_raised("Could not create tag %s" % names.inspect) {
+ return AST::Tag.new(args)
+ }
+ end
+
+ def compobj(name, args = {})
+ args[:file] ||= tempfile()
+ args[:line] ||= rand(100)
+ args[:type] ||= nameobj(name)
+ args[:args] ||= AST::ASTArray.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :children => []
+ )
+ args[:code] ||= AST::ASTArray.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :children => [
+ varobj("%svar" % name, "%svalue" % name),
+ fileobj("/%s" % name)
+ ]
+ )
+ assert_nothing_raised("Could not create compdef %s" % name) {
+ return AST::CompDef.new(args)
+ }
+ end
+
+ def objectdef(type, name, params)
+ assert_nothing_raised("Could not create %s %s" % [type, name]) {
+ return AST::ObjectDef.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :name => stringobj(name),
+ :type => nameobj(type),
+ :params => objectinst(params)
+ )
+ }
+ end
+
+ def fileobj(path, hash = {"owner" => "root"})
+ assert_nothing_raised("Could not create file %s" % path) {
+ return objectdef("file", path, hash)
+# return AST::ObjectDef.new(
+# :file => tempfile(),
+# :line => rand(100),
+# :name => stringobj(path),
+# :type => nameobj("file"),
+# :params => objectinst(hash)
+# )
+ }
+ end
+
+ def nameobj(name)
+ assert_nothing_raised("Could not create name %s" % name) {
+ return AST::Name.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :value => name
+ )
+ }
+ end
+
+ def typeobj(name)
+ assert_nothing_raised("Could not create type %s" % name) {
+ return AST::Type.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :value => name
+ )
+ }
+ end
+
+ def nodedef(name)
+ assert_nothing_raised("Could not create node %s" % name) {
+ return AST::NodeDef.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :names => nameobj(name),
+ :code => AST::ASTArray.new(
+ :children => [
+ varobj("%svar" % name, "%svalue" % name),
+ fileobj("/%s" % name)
+ ]
+ )
+ )
+ }
+ end
+
+ def objectinst(hash)
+ assert_nothing_raised("Could not create object instance") {
+ params = hash.collect { |param, value|
+ objectparam(param, value)
+ }
+ return AST::ObjectInst.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :children => params
+ )
+ }
+ end
+
+ def objectparam(param, value)
+ # Allow them to pass non-strings in
+ if value.is_a?(String)
+ value = stringobj(value)
+ end
+ assert_nothing_raised("Could not create param %s" % param) {
+ return AST::ObjectParam.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :param => nameobj(param),
+ :value => value
+ )
+ }
+ end
+
+ def stringobj(value)
+ AST::String.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :value => value
+ )
+ end
+
+ def varobj(name, value)
+ unless value.is_a? AST
+ value = stringobj(value)
+ end
+ assert_nothing_raised("Could not create %s code" % name) {
+ return AST::VarDef.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :name => nameobj(name),
+ :value => value
+ )
+ }
+ end
+
+ def varref(name)
+ assert_nothing_raised("Could not create %s variable" % name) {
+ return AST::Variable.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :value => name
+ )
+ }
+ end
+
+ def argobj(name, value)
+ assert_nothing_raised("Could not create %s compargument" % name) {
+ return AST::CompArgument.new(
+ :children => [nameobj(name), stringobj(value)]
+ )
+ }
+ end
+
+ def defaultobj(type, params)
+ pary = []
+ params.each { |p,v|
+ pary << AST::ObjectParam.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :param => nameobj(p),
+ :value => stringobj(v)
+ )
+ }
+ past = AST::ASTArray.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :children => pary
+ )
+
+ assert_nothing_raised("Could not create defaults for %s" % type) {
+ return AST::TypeDefaults.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :type => typeobj(type),
+ :params => past
+ )
+ }
+ end
+
+ def taggedobj(name, ftype = :statement)
+ functionobj("tagged", name, ftype)
+ end
+
+ def functionobj(function, name, ftype = :statement)
+ func = nil
+ assert_nothing_raised do
+ func = Puppet::Parser::AST::Function.new(
+ :name => function,
+ :ftype => ftype,
+ :arguments => AST::ASTArray.new(
+ :children => [nameobj(name)]
+ )
+ )
+ end
+
+ return func
+ end
+
+ # This assumes no nodes
+ def assert_creates(manifest, *files)
+ interp = nil
+ assert_nothing_raised {
+ interp = Puppet::Parser::Interpreter.new(
+ :Manifest => manifest,
+ :UseNodes => false
+ )
+ }
+
+ config = nil
+ assert_nothing_raised {
+ config = interp.run(Facter["hostname"].value, {})
+ }
+
+ comp = nil
+ assert_nothing_raised {
+ comp = config.to_type
+ }
+
+ assert_apply(comp)
+
+ files.each do |file|
+ assert(FileTest.exists?(file), "Did not create %s" % file)
+ end
+ end
+
+ def mk_transobject(file = "/etc/passwd")
+ obj = nil
+ assert_nothing_raised {
+ obj = Puppet::TransObject.new("file", file)
+ obj["owner"] = "root"
+ obj["mode"] = "644"
+ }
+
+ return obj
+ end
+
+ def mk_transbucket(*objects)
+ bucket = nil
+ assert_nothing_raised {
+ bucket = Puppet::TransBucket.new
+ bucket.name = "yayname"
+ bucket.type = "yaytype"
+ }
+
+ objects.each { |o| bucket << o }
+
+ return bucket
+ end
+
+ # Make a tree of objects, yielding if desired
+ def mk_transtree(depth = 4, width = 2)
+ top = nil
+ assert_nothing_raised {
+ top = Puppet::TransBucket.new
+ top.name = "top"
+ top.type = "bucket"
+ }
+
+ bucket = top
+
+ file = tempfile()
+ depth.times do |i|
+ objects = []
+ width.times do |j|
+ path = tempfile + i.to_s
+ obj = Puppet::TransObject.new("file", path)
+ obj["owner"] = "root"
+ obj["mode"] = "644"
+
+ # Yield, if they want
+ if block_given?
+ yield(obj, i, j)
+ end
+
+ objects << obj
+ end
+
+ newbucket = mk_transbucket(*objects)
+
+ bucket.push newbucket
+ bucket = newbucket
+ end
+
+ return top
+ end
+
+ # Take a list of AST objects, evaluate them, and return the results
+ def assert_evaluate(children)
+ top = nil
+ assert_nothing_raised("Could not create top object") {
+ top = AST::ASTArray.new(
+ :children => children
+ )
+ }
+
+ trans = nil
+ scope = nil
+ assert_nothing_raised {
+ scope = Puppet::Parser::Scope.new()
+ trans = scope.evaluate(:ast => top)
+ }
+
+ return trans
+ end
+end
+
+class PuppetTestSuite
+ attr_accessor :subdir
+
+ def self.basedir
+ unless defined? @basedir
+ @basedir = File.join($puppetbase, "test")
+ end
+ @basedir
+ end
+
+ def self.list
+ Dir.entries(self.basedir).find_all { |file|
+ path = File.join(@basedir, file)
+ # Data is for storing test data
+ FileTest.directory?(path) and file !~ /^\./ and file != "data"
+ }
+ end
+
+ def initialize(name)
+ path = File.join(self.class.basedir, name)
+ if FileTest.directory?(path)
+ # load each of the files
+ Dir.entries(path).collect { |file|
+ File.join(path,file)
+ }.find_all { |file|
+ FileTest.file?(file) and file =~ /\.rb$/
+ }.sort { |a,b|
+ # in the order they were modified, so the last modified files
+ # are loaded and thus displayed last
+ File.stat(b) <=> File.stat(a)
+ }.each { |file|
+ require file
+ }
+ elsif FileTest.file?(path) && path =~ /\.rb$/
+ require path
+ else
+ puts "TestSuites are directories or files containing test cases"
+ puts "no such directory: %s" % path
+ exit(65)
+ end
+ end
+end
+
+# a list of files that we can parse for testing
+def textfiles
+ textdir = File.join($puppetbase,"examples","code", "snippets")
+ Dir.entries(textdir).reject { |f|
+ f =~ /^\./ or f =~ /fail/
+ }.each { |f|
+ yield File.join(textdir, f)
+ }
+end
+
+def failers
+ textdir = File.join($puppetbase,"examples","code", "failers")
+ # only parse this one file now
+ files = Dir.entries(textdir).reject { |file|
+ file =~ %r{\.swp}
+ }.reject { |file|
+ file =~ %r{\.disabled}
+ }.collect { |file|
+ File.join(textdir,file)
+ }.find_all { |file|
+ FileTest.file?(file)
+ }.sort.each { |file|
+ Puppet.debug "Processing %s" % file
+ yield file
+ }
+end
+
+# $Id$
diff --git a/test/lib/scaffold.rb b/test/lib/scaffold.rb
new file mode 100644
index 000000000..dca1e740c
--- /dev/null
+++ b/test/lib/scaffold.rb
@@ -0,0 +1,94 @@
+require 'support/helpers'
+
+module PuppetTestScaffold
+ include PuppetTestSupport::Helpers
+
+ def setup
+ @memoryatstart = Puppet::Util.memory
+ if defined? @@testcount
+ @@testcount += 1
+ else
+ @@testcount = 0
+ end
+
+ @configpath = File.join(tmpdir,
+ self.class.to_s + "configdir" + @@testcount.to_s + "/"
+ )
+
+ unless defined? $user and $group
+ $user = nonrootuser().uid.to_s
+ $group = nonrootgroup().gid.to_s
+ end
+ Puppet[:user] = $user
+ Puppet[:group] = $group
+
+ Puppet[:confdir] = @configpath
+ Puppet[:vardir] = @configpath
+
+ unless File.exists?(@configpath)
+ Dir.mkdir(@configpath)
+ end
+
+ @@tmpfiles = [@configpath, tmpdir()]
+ @@tmppids = []
+
+ @@cleaners = []
+
+ if $0 =~ /.+\.rb/ or Puppet[:debug]
+ Puppet::Log.newdestination :console
+ Puppet::Log.level = :debug
+ #$VERBOSE = 1
+ Puppet.info @method_name
+ else
+ Puppet::Log.close
+ Puppet::Log.newdestination tempfile()
+ Puppet[:httplog] = tempfile()
+ end
+
+ Puppet[:ignoreschedules] = true
+ end
+
+ def teardown
+ stopservices
+
+ @@cleaners.each { |cleaner| cleaner.call() }
+
+ @@tmpfiles.each { |file|
+ if FileTest.exists?(file)
+ system("chmod -R 755 %s" % file)
+ system("rm -rf %s" % file)
+ end
+ }
+ @@tmpfiles.clear
+
+ @@tmppids.each { |pid|
+ %x{kill -INT #{pid} 2>/dev/null}
+ }
+
+ @@tmppids.clear
+ Puppet::Type.allclear
+ Puppet::Storage.clear
+ Puppet::Rails.clear
+ Puppet.clear
+
+ @memoryatend = Puppet::Util.memory
+ diff = @memoryatend - @memoryatstart
+
+ if diff > 1000
+ Puppet.info "%s#%s memory growth (%s to %s): %s" %
+ [self.class, @method_name, @memoryatstart, @memoryatend, diff]
+ end
+
+ # reset all of the logs
+ Puppet::Log.close
+
+ # Just in case there are processes waiting to die...
+ Process.waitall
+ if File.stat("/dev/null").mode & 007777 != 0666
+ File.open("/tmp/nullfailure", "w") { |f|
+ f.puts self.class
+ }
+ exit(74)
+ end
+ end
+end
diff --git a/test/lib/servertest.rb b/test/lib/servertest.rb
new file mode 100644
index 000000000..a1c498467
--- /dev/null
+++ b/test/lib/servertest.rb
@@ -0,0 +1,68 @@
+require 'scaffold'
+
+module ServerTest
+ include PuppetTestScaffold
+ def setup
+ super
+
+ if defined? @@port
+ @@port += 1
+ else
+ @@port = 20000
+ end
+ end
+
+ # create a simple manifest that just creates a file
+ def mktestmanifest
+ file = File.join(Puppet[:confdir], "%ssite.pp" % (self.class.to_s + "test"))
+ #@createdfile = File.join(tmpdir(), self.class.to_s + "manifesttesting" +
+ # "_" + @method_name)
+ @createdfile = tempfile()
+
+ File.open(file, "w") { |f|
+ f.puts "file { \"%s\": ensure => file, mode => 755 }\n" % @createdfile
+ }
+
+ @@tmpfiles << @createdfile
+ @@tmpfiles << file
+
+ return file
+ end
+
+ # create a server, forked into the background
+ def mkserver(handlers = nil)
+ # our default handlers
+ unless handlers
+ handlers = {
+ :CA => {}, # so that certs autogenerate
+ :Master => {
+ :Manifest => mktestmanifest(),
+ :UseNodes => false
+ },
+ }
+ end
+
+ # then create the actual server
+ server = nil
+ assert_nothing_raised {
+ server = Puppet::Server.new(
+ :Port => @@port,
+ :Handlers => handlers
+ )
+ }
+
+ # fork it
+ spid = fork {
+ trap(:INT) { server.shutdown }
+ server.start
+ }
+
+ # and store its pid for killing
+ @@tmppids << spid
+
+ # give the server a chance to do its thing
+ sleep 1
+ return spid
+ end
+
+end
diff --git a/test/lib/support.rb b/test/lib/support.rb
new file mode 100644
index 000000000..2d34ecb42
--- /dev/null
+++ b/test/lib/support.rb
@@ -0,0 +1,8 @@
+module PuppetTestSupport
+end
+
+require 'support/assertions'
+require 'support/file'
+require 'support/helpers'
+require 'support/parser'
+require 'support/utils'
diff --git a/test/lib/support/assertions.rb b/test/lib/support/assertions.rb
new file mode 100644
index 000000000..444b0e4ec
--- /dev/null
+++ b/test/lib/support/assertions.rb
@@ -0,0 +1,67 @@
+module PuppetTestSupport
+ module Assertions
+
+ def assert_rollback_events(events, trans, msg = nil)
+ run_events(:rollback, events, trans, msg)
+ end
+
+ def assert_events(events, *items)
+ trans = nil
+ comp = nil
+ msg = nil
+
+ unless events.is_a? Array
+ raise Puppet::DevError, "Incorrect call of assert_events"
+ end
+ if items[-1].is_a? String
+ msg = items.pop
+ end
+
+ remove_comp = false
+ # They either passed a comp or a list of items.
+ if items[0].is_a? Puppet.type(:component)
+ comp = items.shift
+ else
+ comp = newcomp(items[0].title, *items)
+ remove_comp = true
+ end
+ msg ||= comp.title
+ assert_nothing_raised("Component %s failed" % [msg]) {
+ trans = comp.evaluate
+ }
+
+ run_events(:evaluate, trans, events, msg)
+
+ if remove_comp
+ Puppet.type(:component).delete(comp)
+ end
+
+ return trans
+ end
+
+ # A simpler method that just applies what we have.
+ def assert_apply(*objects)
+ if objects[0].is_a?(Puppet.type(:component))
+ comp = objects.shift
+ unless objects.empty?
+ objects.each { |o| comp.push o }
+ end
+ else
+ comp = newcomp(*objects)
+ end
+ trans = nil
+
+ assert_nothing_raised("Failed to create transaction") {
+ trans = comp.evaluate
+ }
+
+ events = nil
+ assert_nothing_raised("Failed to evaluate transaction") {
+ events = trans.evaluate.collect { |e| e.event }
+ }
+ Puppet.type(:component).delete(comp)
+ events
+ end
+
+ end
+end
diff --git a/test/lib/support/file.rb b/test/lib/support/file.rb
new file mode 100644
index 000000000..f8d1e0c00
--- /dev/null
+++ b/test/lib/support/file.rb
@@ -0,0 +1,228 @@
+module PuppetTestSupport
+ module File
+ def cycle(comp)
+ trans = nil
+ assert_nothing_raised {
+ trans = comp.evaluate
+ }
+ assert_nothing_raised {
+ trans.evaluate
+ }
+ end
+
+ def randlist(list)
+ num = rand(4)
+ if num == 0
+ num = 1
+ end
+ set = []
+
+ ret = []
+ num.times { |index|
+ item = list[rand(list.length)]
+ if set.include?(item)
+ redo
+ end
+
+ ret.push item
+ }
+ return ret
+ end
+
+ def mkranddirsandfiles(dirs = nil,files = nil,depth = 3)
+ if depth < 0
+ return
+ end
+
+ unless dirs
+ dirs = %w{This Is A Set Of Directories}
+ end
+
+ unless files
+ files = %w{and this is a set of files}
+ end
+
+ tfiles = randlist(files)
+ tdirs = randlist(dirs)
+
+ tfiles.each { |file|
+ File.open(file, "w") { |of|
+ 4.times {
+ of.puts rand(100)
+ }
+ }
+ }
+
+ tdirs.each { |dir|
+ # it shouldn't already exist, but...
+ unless FileTest.exists?(dir)
+ Dir.mkdir(dir)
+ FileUtils.cd(dir) {
+ mkranddirsandfiles(dirs,files,depth - 1)
+ }
+ end
+ }
+ end
+
+ def file_list(dir)
+ list = nil
+ FileUtils.cd(dir) {
+ list = %x{find . 2>/dev/null}.chomp.split(/\n/)
+ }
+ return list
+ end
+
+ def assert_trees_equal(fromdir,todir)
+ assert(FileTest.directory?(fromdir))
+ assert(FileTest.directory?(todir))
+
+ # verify the file list is the same
+ fromlist = nil
+ FileUtils.cd(fromdir) {
+ fromlist = %x{find . 2>/dev/null}.chomp.split(/\n/).reject { |file|
+ ! FileTest.readable?(file)
+ }.sort
+ }
+ tolist = file_list(todir).sort
+
+ fromlist.sort.zip(tolist.sort).each { |a,b|
+ assert_equal(a, b,
+ "Fromfile %s with length %s does not match tofile %s with length %s" %
+ [a, fromlist.length, b, tolist.length])
+ }
+ #assert_equal(fromlist,tolist)
+
+ # and then do some verification that the files are actually set up
+ # the same
+ checked = 0
+ fromlist.each_with_index { |file,i|
+ fromfile = File.join(fromdir,file)
+ tofile = File.join(todir,file)
+ fromstat = File.stat(fromfile)
+ tostat = File.stat(tofile)
+ [:ftype,:gid,:mode,:uid].each { |method|
+ assert_equal(
+ fromstat.send(method),
+ tostat.send(method)
+ )
+
+ next if fromstat.ftype == "directory"
+ if checked < 10 and i % 3 == 0
+ from = File.open(fromfile) { |f| f.read }
+ to = File.open(tofile) { |f| f.read }
+
+ assert_equal(from,to)
+ checked += 1
+ end
+ }
+ }
+ end
+
+ def random_files(dir)
+ checked = 0
+ list = file_list(dir)
+ list.reverse.each_with_index { |file,i|
+ path = File.join(dir,file)
+ stat = File.stat(dir)
+ if checked < 10 and (i % 3) == 2
+ unless yield path
+ next
+ end
+ checked += 1
+ end
+ }
+ end
+
+ def delete_random_files(dir)
+ deleted = []
+ random_files(dir) { |file|
+ stat = File.stat(file)
+ begin
+ if stat.ftype == "directory"
+ false
+ else
+ deleted << file
+ File.unlink(file)
+ true
+ end
+ rescue => detail
+ # we probably won't be able to open our own secured files
+ puts detail
+ false
+ end
+ }
+
+ return deleted
+ end
+
+ def add_random_files(dir)
+ added = []
+ random_files(dir) { |file|
+ stat = File.stat(file)
+ begin
+ if stat.ftype == "directory"
+ name = File.join(file,"file" + rand(100).to_s)
+ File.open(name, "w") { |f|
+ f.puts rand(10)
+ }
+ added << name
+ else
+ false
+ end
+ rescue => detail
+ # we probably won't be able to open our own secured files
+ puts detail
+ false
+ end
+ }
+ return added
+ end
+
+ def modify_random_files(dir)
+ modded = []
+ random_files(dir) { |file|
+ stat = File.stat(file)
+ begin
+ if stat.ftype == "directory"
+ false
+ else
+ File.open(file, "w") { |f|
+ f.puts rand(10)
+ }
+ modded << name
+ true
+ end
+ rescue => detail
+ # we probably won't be able to open our own secured files
+ puts detail
+ false
+ end
+ }
+ return modded
+ end
+
+ def readonly_random_files(dir)
+ modded = []
+ random_files(dir) { |file|
+ stat = File.stat(file)
+ begin
+ if stat.ftype == "directory"
+ File.new(file).chmod(0111)
+ else
+ File.new(file).chmod(0000)
+ end
+ modded << file
+ rescue => detail
+ # we probably won't be able to open our own secured files
+ puts detail
+ false
+ end
+ }
+ return modded
+ end
+
+ def conffile
+ File.join($puppetbase,"examples/root/etc/configfile")
+ end
+ end
+end
diff --git a/test/lib/support/helpers.rb b/test/lib/support/helpers.rb
new file mode 100644
index 000000000..72dae39d6
--- /dev/null
+++ b/test/lib/support/helpers.rb
@@ -0,0 +1,60 @@
+module PuppetTestSupport
+ module Helpers
+ def nonrootuser
+ Etc.passwd { |user|
+ if user.uid != Process.uid and user.uid > 0
+ return user
+ end
+ }
+ end
+
+ def nonrootgroup
+ Etc.group { |group|
+ if group.gid != Process.gid and group.gid > 0
+ return group
+ end
+ }
+ end
+
+ def cleanup(&block)
+ @@cleaners << block
+ end
+
+ def tempfile
+ if defined? @@tmpfilenum
+ @@tmpfilenum += 1
+ else
+ @@tmpfilenum = 1
+ end
+
+ f = File.join(self.tmpdir(), self.class.to_s + "_" + @method_name +
+ @@tmpfilenum.to_s)
+ @@tmpfiles << f
+ return f
+ end
+
+ def tstdir
+ tempfile()
+ end
+
+ def tmpdir
+ unless defined? @tmpdir and @tmpdir
+ @tmpdir = case Facter["operatingsystem"].value
+ when "Darwin": "/private/tmp"
+ when "SunOS": "/var/tmp"
+ else
+ "/tmp"
+ end
+
+
+ @tmpdir = File.join(@tmpdir, "puppettesting")
+
+ unless File.exists?(@tmpdir)
+ FileUtils.mkdir_p(@tmpdir)
+ File.chmod(01777, @tmpdir)
+ end
+ end
+ @tmpdir
+ end
+ end
+end
diff --git a/test/lib/support/parser.rb b/test/lib/support/parser.rb
new file mode 100644
index 000000000..fcad02501
--- /dev/null
+++ b/test/lib/support/parser.rb
@@ -0,0 +1,344 @@
+module PuppetTestSupport
+ module Parser
+ AST = Puppet::Parser::AST
+
+ def astarray(*args)
+ AST::ASTArray.new(
+ :children => args
+ )
+ end
+
+ def classobj(name, args = {})
+ args[:type] ||= nameobj(name)
+ args[:code] ||= AST::ASTArray.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :children => [
+ varobj("%svar" % name, "%svalue" % name),
+ fileobj("/%s" % name)
+ ]
+ )
+ assert_nothing_raised("Could not create class %s" % name) {
+ return AST::ClassDef.new(args)
+ }
+ end
+
+ def tagobj(*names)
+ args = {}
+ newnames = names.collect do |name|
+ if name.is_a? AST
+ name
+ else
+ nameobj(name)
+ end
+ end
+ args[:type] = astarray(*newnames)
+ assert_nothing_raised("Could not create tag %s" % names.inspect) {
+ return AST::Tag.new(args)
+ }
+ end
+
+ def compobj(name, args = {})
+ args[:file] ||= tempfile()
+ args[:line] ||= rand(100)
+ args[:type] ||= nameobj(name)
+ args[:args] ||= AST::ASTArray.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :children => []
+ )
+ args[:code] ||= AST::ASTArray.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :children => [
+ varobj("%svar" % name, "%svalue" % name),
+ fileobj("/%s" % name)
+ ]
+ )
+ assert_nothing_raised("Could not create compdef %s" % name) {
+ return AST::CompDef.new(args)
+ }
+ end
+
+ def objectdef(type, name, params)
+ assert_nothing_raised("Could not create %s %s" % [type, name]) {
+ return AST::ObjectDef.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :name => stringobj(name),
+ :type => nameobj(type),
+ :params => objectinst(params)
+ )
+ }
+ end
+
+ def fileobj(path, hash = {"owner" => "root"})
+ assert_nothing_raised("Could not create file %s" % path) {
+ return objectdef("file", path, hash)
+ # return AST::ObjectDef.new(
+ # :file => tempfile(),
+ # :line => rand(100),
+ # :name => stringobj(path),
+ # :type => nameobj("file"),
+ # :params => objectinst(hash)
+ # )
+ }
+ end
+
+ def nameobj(name)
+ assert_nothing_raised("Could not create name %s" % name) {
+ return AST::Name.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :value => name
+ )
+ }
+ end
+
+ def typeobj(name)
+ assert_nothing_raised("Could not create type %s" % name) {
+ return AST::Type.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :value => name
+ )
+ }
+ end
+
+ def nodedef(name)
+ assert_nothing_raised("Could not create node %s" % name) {
+ return AST::NodeDef.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :names => nameobj(name),
+ :code => AST::ASTArray.new(
+ :children => [
+ varobj("%svar" % name, "%svalue" % name),
+ fileobj("/%s" % name)
+ ]
+ )
+ )
+ }
+ end
+
+ def objectinst(hash)
+ assert_nothing_raised("Could not create object instance") {
+ params = hash.collect { |param, value|
+ objectparam(param, value)
+ }
+ return AST::ObjectInst.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :children => params
+ )
+ }
+ end
+
+ def objectparam(param, value)
+ # Allow them to pass non-strings in
+ if value.is_a?(String)
+ value = stringobj(value)
+ end
+ assert_nothing_raised("Could not create param %s" % param) {
+ return AST::ObjectParam.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :param => nameobj(param),
+ :value => value
+ )
+ }
+ end
+
+ def stringobj(value)
+ AST::String.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :value => value
+ )
+ end
+
+ def varobj(name, value)
+ unless value.is_a? AST
+ value = stringobj(value)
+ end
+ assert_nothing_raised("Could not create %s code" % name) {
+ return AST::VarDef.new(
+ :file => tempfile(),
+ :line => rand(100),
+ :name => nameobj(name),
+ :value => value
+ )
+ }
+ end
+
+ def varref(name)
+ assert_nothing_raised("Could not create %s variable" % name) {
+ return AST::Variable.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :value => name
+ )
+ }
+ end
+
+ def argobj(name, value)
+ assert_nothing_raised("Could not create %s compargument" % name) {
+ return AST::CompArgument.new(
+ :children => [nameobj(name), stringobj(value)]
+ )
+ }
+ end
+
+ def defaultobj(type, params)
+ pary = []
+ params.each { |p,v|
+ pary << AST::ObjectParam.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :param => nameobj(p),
+ :value => stringobj(v)
+ )
+ }
+ past = AST::ASTArray.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :children => pary
+ )
+
+ assert_nothing_raised("Could not create defaults for %s" % type) {
+ return AST::TypeDefaults.new(
+ :file => __FILE__,
+ :line => __LINE__,
+ :type => typeobj(type),
+ :params => past
+ )
+ }
+ end
+
+ def taggedobj(name, ftype = :statement)
+ functionobj("tagged", name, ftype)
+ end
+
+ def functionobj(function, name, ftype = :statement)
+ func = nil
+ assert_nothing_raised do
+ func = Puppet::Parser::AST::Function.new(
+ :name => function,
+ :ftype => ftype,
+ :arguments => AST::ASTArray.new(
+ :children => [nameobj(name)]
+ )
+ )
+ end
+
+ return func
+ end
+
+ # This assumes no nodes
+ def assert_creates(manifest, *files)
+ interp = nil
+ assert_nothing_raised {
+ interp = Puppet::Parser::Interpreter.new(
+ :Manifest => manifest,
+ :UseNodes => false
+ )
+ }
+
+ config = nil
+ assert_nothing_raised {
+ config = interp.run(Facter["hostname"].value, {})
+ }
+
+ comp = nil
+ assert_nothing_raised {
+ comp = config.to_type
+ }
+
+ assert_apply(comp)
+
+ files.each do |file|
+ assert(FileTest.exists?(file), "Did not create %s" % file)
+ end
+ end
+
+ def mk_transobject(file = "/etc/passwd")
+ obj = nil
+ assert_nothing_raised {
+ obj = Puppet::TransObject.new("file", file)
+ obj["owner"] = "root"
+ obj["mode"] = "644"
+ }
+
+ return obj
+ end
+
+ def mk_transbucket(*objects)
+ bucket = nil
+ assert_nothing_raised {
+ bucket = Puppet::TransBucket.new
+ bucket.name = "yayname"
+ bucket.type = "yaytype"
+ }
+
+ objects.each { |o| bucket << o }
+
+ return bucket
+ end
+
+ # Make a tree of objects, yielding if desired
+ def mk_transtree(depth = 4, width = 2)
+ top = nil
+ assert_nothing_raised {
+ top = Puppet::TransBucket.new
+ top.name = "top"
+ top.type = "bucket"
+ }
+
+ bucket = top
+
+ file = tempfile()
+ depth.times do |i|
+ objects = []
+ width.times do |j|
+ path = tempfile + i.to_s
+ obj = Puppet::TransObject.new("file", path)
+ obj["owner"] = "root"
+ obj["mode"] = "644"
+
+ # Yield, if they want
+ if block_given?
+ yield(obj, i, j)
+ end
+
+ objects << obj
+ end
+
+ newbucket = mk_transbucket(*objects)
+
+ bucket.push newbucket
+ bucket = newbucket
+ end
+
+ return top
+ end
+
+ # Take a list of AST objects, evaluate them, and return the results
+ def assert_evaluate(children)
+ top = nil
+ assert_nothing_raised("Could not create top object") {
+ top = AST::ASTArray.new(
+ :children => children
+ )
+ }
+
+ trans = nil
+ scope = nil
+ assert_nothing_raised {
+ scope = Puppet::Parser::Scope.new()
+ trans = scope.evaluate(:ast => top)
+ }
+
+ return trans
+ end
+ end
+end
diff --git a/test/lib/support/utils.rb b/test/lib/support/utils.rb
new file mode 100644
index 000000000..9aea05aff
--- /dev/null
+++ b/test/lib/support/utils.rb
@@ -0,0 +1,146 @@
+module PuppetTestSupport
+ module Utils
+ def gcdebug(type)
+ Puppet.warning "%s: %s" % [type, ObjectSpace.each_object(type) { |o| }]
+ end
+
+ #
+ # TODO: I think this method needs to be renamed to something a little more explanatory.
+ #
+
+ def newobj(type, name, hash)
+ transport = Puppet::TransObject.new(name, "file")
+ transport[:path] = path
+ transport[:ensure] = "file"
+ assert_nothing_raised {
+ file = transport.to_type
+ }
+ end
+
+ # stop any services that might be hanging around
+ def stopservices
+ if stype = Puppet::Type.type(:service)
+ stype.each { |service|
+ service[:ensure] = :stopped
+ service.evaluate
+ }
+ end
+ end
+
+ # TODO: rewrite this to use the 'etc' module.
+
+ def setme
+ # retrieve the user name
+ id = %x{id}.chomp
+ if id =~ /uid=\d+\(([^\)]+)\)/
+ @me = $1
+ else
+ puts id
+ end
+ unless defined? @me
+ raise "Could not retrieve user name; 'id' did not work"
+ end
+ end
+
+ def run_events(type, trans, events, msg)
+ case type
+ when :evaluate, :rollback: # things are hunky-dory
+ else
+ raise Puppet::DevError, "Incorrect run_events type"
+ end
+
+ method = type
+
+ newevents = nil
+ assert_nothing_raised("Transaction %s %s failed" % [type, msg]) {
+ newevents = trans.send(method).reject { |e| e.nil? }.collect { |e|
+ e.event
+ }
+ }
+
+ assert_equal(events, newevents, "Incorrect %s %s events" % [type, msg])
+
+ return trans
+ end
+
+ # If there are any fake data files, retrieve them
+ def fakedata(dir)
+ ary = [$puppetbase, "test"]
+ ary += dir.split("/")
+ dir = File.join(ary)
+
+ unless FileTest.exists?(dir)
+ raise Puppet::DevError, "No fakedata dir %s" % dir
+ end
+ files = Dir.entries(dir).reject { |f| f =~ /^\./ }.collect { |f|
+ File.join(dir, f)
+ }
+
+ return files
+ end
+
+ def fakefile(name)
+ ary = [$puppetbase, "test"]
+ ary += name.split("/")
+ file = File.join(ary)
+ unless FileTest.exists?(file)
+ raise Puppet::DevError, "No fakedata file %s" % file
+ end
+ return file
+ end
+
+ # wrap how to retrieve the masked mode
+ def filemode(file)
+ File.stat(file).mode & 007777
+ end
+
+ def memory
+ Puppet::Util.memory
+ end
+
+ # a list of files that we can parse for testing
+ def textfiles
+ textdir = File.join($puppetbase,"examples","code", "snippets")
+ Dir.entries(textdir).reject { |f|
+ f =~ /^\./ or f =~ /fail/
+ }.each { |f|
+ yield File.join(textdir, f)
+ }
+ end
+
+ def failers
+ textdir = File.join($puppetbase,"examples","code", "failers")
+ # only parse this one file now
+ files = Dir.entries(textdir).reject { |file|
+ file =~ %r{\.swp}
+ }.reject { |file|
+ file =~ %r{\.disabled}
+ }.collect { |file|
+ File.join(textdir,file)
+ }.find_all { |file|
+ FileTest.file?(file)
+ }.sort.each { |file|
+ Puppet.debug "Processing %s" % file
+ yield file
+ }
+ end
+
+ def newcomp(*ary)
+ name = nil
+ if ary[0].is_a?(String)
+ name = ary.shift
+ else
+ name = ary[0].title
+ end
+
+ comp = Puppet.type(:component).create(
+ :name => name
+ )
+ ary.each { |item|
+ comp.push item
+ }
+
+ return comp
+ end
+ end
+end