diff options
| author | erikh <erikh@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-09-15 04:00:38 +0000 |
|---|---|---|
| committer | erikh <erikh@980ebf18-57e1-0310-9a29-db15c13687c0> | 2006-09-15 04:00:38 +0000 |
| commit | 4cd37ade1574a326f9ce267af2edc61a78deea86 (patch) | |
| tree | 1093cba18d4c7d7d4170c255db442f3dc0c774a7 /test/lib | |
| parent | 4897995e4cbe4ec3e452de50404799cfc27ed90b (diff) | |
| download | puppet-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.rb | 120 | ||||
| -rw-r--r-- | test/lib/fakes.rb | 99 | ||||
| -rw-r--r-- | test/lib/puppettest.rb | 1277 | ||||
| -rw-r--r-- | test/lib/scaffold.rb | 94 | ||||
| -rw-r--r-- | test/lib/servertest.rb | 68 | ||||
| -rw-r--r-- | test/lib/support.rb | 8 | ||||
| -rw-r--r-- | test/lib/support/assertions.rb | 67 | ||||
| -rw-r--r-- | test/lib/support/file.rb | 228 | ||||
| -rw-r--r-- | test/lib/support/helpers.rb | 60 | ||||
| -rw-r--r-- | test/lib/support/parser.rb | 344 | ||||
| -rw-r--r-- | test/lib/support/utils.rb | 146 |
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 |
