# Name: QuickLookTool.rb # Author: Steve Peralta # Date: 9/16/07 # Description: QuickLookTest and related classes $kQuickLookToolVers = "1.8" # ------------------------------------------------- # To create a test tool based on QuickLookTool: # ------------------------------------------------- # 1. Define a subclass of QuickLookTest # # -- at minimum: # # * In initialize(), call setTestCases() passing an array of test # classes specific to your test program. For example: # class MyServiceTool < QuickLookTool # def initialize # super("myservice_quicklook", "1.0", "MyService", "myserviced", "MyService Quicklook Test Suite") # self.setTestCases([TestCase1, TestCase2, ...]) # ... # -- optionally: # # * In initialize(), call setPrereqFiles() with an array of files # that must exist for the selected tests to be performed. For example: # def initialize # ... # self.setPrereqFiles(["/usr/libexec/myserviced", "/usr/bin/myservicetool"]) # ... # # * Override doTestLoopSetup to perform actions prior to test-loop execution # # * Override doTestLoopCleanup to perform actions after test-loop execution. # # * Override canRun? to verify non-file prerequisites that would prevent # your test from running. If your prerequisites consist only of files, # then use setPrereqFiles() as outlined above. # # 2. Define sub-classes of QLTestCase for each individual test case # # -- For tests that consist of arbitrary shell commands, subclass QLTestCase and # initialize with a standard QLCommand instance. For example: # class TestCase1 < QLTestCase # def initialize(seq, qltool) # qlcmd = QLCommand.new(qltool, "/usr/bin/sometest --dothis --dothat", "did it!") # super(seq, qltool, "Standard", "Do some test", qlcmd) # ... # # -- For tests based on serveradmin commands, subclass QLTestCase and initialize with # an instance of SACommand. Typically, tests of this type will create in instance # of the more specialize SACommand sub-class SAStateCommand or SASettingsCommand. # For example: # class StartServiceTest < SATestCase # def initialize(seq, qltool) # qlcmd = SAStateCommand.new(qltool, SAStateCommand::STATE_START) # super(seq, qltool, "Standard", "Start MyExample service", qlcmd) # ... # class SetFoobarSizeTest < SATestCase # def initialize(seq, qltool) # qlcmd = SASettingsCommand.new(qltool, "FoobarKey", "777") # super(seq, qltool, "Standard", "Set Foobar to default value", qlcmd) # ... # # -- If your test is too complicated to fit in a single shell command, consider # generating an external custom script and letting doTest run that as the command. # Your script can send text to STDOUT that can be matched with the "expected" text. # # -- If all else fails, override QLTestCase::doTest, but don't forget to support # the XILog hooks in your override method. # # ---------------------------------------- # To start your the test tool (MAIN): # ---------------------------------------- # # 1. create an instance of your QuickLookTest class # 3. call my_class.runCommand and exit with the failed/success status # 4. that's all! # # Example MAIN: # qltool = MyServiceTool.new # status = qltool.runCommand ? 0 : 1 # exit status # # ---------------------------------------- # Running your tool # ---------------------------------------- # # The QuickLookTool class supports a number of built-in options. # Run 'myservice_quicklook --help' for a list of commands and options # supported by the QuickLookTool class or browse the parseOptions # method for details. # #------------------------------------------------------------ # QuickLookTool # # Abstract base class implementation for any Server # Admin-based quicklook test. QuickLookTool manages # quicklook test files, logging, error messages, and # other common utility methods. #------------------------------------------------------------ class QuickLookTool TOOL_VERSION = $kQuickLookToolVers BANNER_BORDER = "============================================================" #---------------------------------------- def initialize(toolName, toolVersion, serviceDesc, serviceID, startBanner) @toolName = toolName @toolVersion = toolVersion @serviceDesc = serviceDesc @serviceID = serviceID @logDir = "/Library/Logs/#{@toolName}" @testLogPath = "#{@logDir}/#{@toolName}.log" @prereqFiles = nil @testCases = nil @startBanner = startBanner @testLog = nil @debugMode = false @toolCommand = nil end #---------------------------------------- def toolName return @toolName end #---------------------------------------- def serviceDesc return @serviceDesc end #---------------------------------------- def serviceID return @serviceID end #---------------------------------------- def logDir return @logDir end #---------------------------------------- def testLogPath return @testLogPath end #---------------------------------------- def debugMode return @debugMode end #---------------------------------------- def toolCommand return @toolCommand end #---------------------------------------- def toolVersion return @toolVersion end #---------------------------------------- def prereqFiles return @prereqFiles end #---------------------------------------- def setPrereqFiles(flist) @prereqFiles = flist end #---------------------------------------- def testCases return @testCases end #---------------------------------------- def setTestCases(tlist) @testCases = tlist end #---------------------------------------- def isUserRoot? return true if self.debugMode my_uid = Process.uid return true if my_uid == 0 STDERR.print "\n#{self.toolName} must be run as root! (current user ID: #{my_uid})\n\n" return false end #---------------------------------------- def getTime `date '+%Y/%m/%d %H:%M:%S'`.chomp! end #---------------------------------------- def createLogFile return @testLog = File.open(@testLogPath, "a") if File.exists?(@testLogPath) STDOUT.puts "creating log file" @testLog = File.new(@testLogPath, "a", 0666) self.logMessage("") self.logMessage("******") self.logMessage("") self.logMessage("created new log") return @testLog end #---------------------------------------- def createTestDir return if File.exists?(@logDir) Dir.mkdir(@logDir, 0777) if !File.exists?(@logDir) then self.showError("Failed to create quicklook test directory: #{@logDir}") exit end end #---------------------------------------- def removeDirectory(dPath) `rm -rf #{dPath}` if File.exists?(dPath) end #---------------------------------------- def removeFiles(fList) fList.each do |aFile| File.delete(aFile) if File.exists?(aFile) end end #---------------------------------------- def readFromFile(fpath) return nil if !File.exists?(fpath) return File.read(fpath).chomp end #---------------------------------------- def writeToFile(fpath, stringVal) File.delete(fpath) if File.exists?(fpath) f = File.new(fpath, "w") if stringVal.length > 0 f.puts(stringVal) else f.print(stringVal) end f.close end #---------------------------------------- def copyFileToConsole(fpath) data = self.readFromFile(fpath) STDOUT.puts(data) end #---------------------------------------- def showError(aMsg) errMsg = "** Error -- #{aMsg}" STDOUT.puts "" STDOUT.puts errMsg self.logMessage(errMsg) end #---------------------------------------- def logMessage(aMsg) return if @testLog == nil @testLog.puts "#{self.getTime} #{aMsg}" end #---------------------------------------- def logAndShow(aMsg) STDOUT.puts aMsg self.logMessage(aMsg) end #---------------------------------------- def logAndShowItems(aMsg, items) outMsg = aMsg sep = items.size > 1 ? ', ' : '' items.each_index do |i| outMsg = "#{outMsg}#{sep}" if i > 0 outMsg = "#{outMsg}#{items[i]}" end STDOUT.puts outMsg self.logMessage(outMsg) end #---------------------------------------- def logFileData(fpath) return if !File.exists?(fpath) data = File.read(fpath).chomp self.logMessage(data) end #---------------------------------------- def getHardwareProfile hwprofile = Hash.new sp_out = `system_profiler SPHardwareDataType`.chomp sp_items = sp_out.split("\n") sp_items.each_index do |i| it = sp_items[i].split(": ") next if it == nil || it[0] == nil || it[1] == nil || it[0].length < 1 key = it[0].strip value = it[1].strip hwprofile[key] = value end return hwprofile end #---------------------------------------- def getHardwareProfileDescription hwp = getHardwareProfile return "\" if hwp.length < 1 mname = hwp["Model Name"] mid = hwp["Model Identifier"] pname = hwp["Processor Name"] pspeed = hwp["Processor Speed"] ncores = hwp["Total Number Of Cores"] nL2 = hwp["L2 Cache (per processor)"] mem = hwp["Memory"] bspeed = hwp["Bus Speed"] rvers = hwp["Boot ROM Version"] svers = hwp["SMC Version"] snum = hwp["Serial Number"] return "#{mname} (#{pname}) #{pspeed}; #{mem} RAM" end #---------------------------------------- def parseOptions # # Parse command line options and set up for runCommand # require 'optparse' require 'optparse/time' require 'ostruct' options = OpenStruct.new options.vers = false options.help = false options.helptext = "" options.list = false options.tests = "" options.groups = "" options.xilog = false options.xiowner = "" options.xiconfig = "" options.install = false options.restore = false options.debug = false OptionParser.new do |opts| opts.banner = "Usage: #{@toolName} [options]" opts.on_head("-h", "--help", "Show this message") do |he| options.help = he options.helptext = opts end opts.on("-l", "--list", "List available test cases") do |lt| options.list = lt end opts.on("-g:", "--groups", "Run only test cases from selected group(s) [all]") do |tg| options.groups << tg end opts.on("-t:", "--tests", "Run only selected test case(s) [all]") do |tc| options.tests << tc end opts.on("-x", "--[no-]xilog", "Enable XILog [disabled]") do |xl| options.xilog = xl end opts.on("-o:", "--owner", "XILog Test Owner [required for --xilog option]") do |xo| options.xiowner << xo end opts.on("-c:", "--config", "Test configuration description") do |cf| options.xiconfig << cf end opts.on("-i", "--install", "Install RubyCocoa-XILog bridge file") do |ib| options.install = ib end opts.on("-r", "--restore", "Restore service default settings") do |rs| options.restore = rs end opts.on("-d", "--[no-]debug", "Run simulated test for script debugging [disabled]") do |db| options.debug = db end opts.on_tail("-v", "--version", "Show tool version") do |ve| options.vers = ve end end.parse! @debugMode = options.debug @toolCommand = ShowHelpCommand.new(self, options.helptext, @startBanner) if options.help @toolCommand = ShowVersionCommand.new(self, @startBanner) if options.vers @toolCommand = InstallBridgeCommand.new(self, "XILog") if options.install @toolCommand = RestoreSettingsCommand.new(self) if options.restore return if @toolCommand != nil selTests = nil selGroups = options.groups.length > 0 ? options.groups.split(',') : nil if selGroups == nil then selTests = options.tests.length > 0 ? options.tests.split(',') : nil end @toolCommand = ShowListCommand.new(self, selGroups, selTests) if options.list return if @toolCommand != nil xilogger = nil if options.xilog then if options.xiowner.length < 1 then STDOUT.print "\nEnter XILog tool owner (ex: com.apple.myteam): " options.xiowner = STDIN.gets.chomp if options.xiowner.length < 1 then self.showError("Missing XILog tool owner (REQUIRED)") @toolCommand = ShowHelpCommand.new(self, nil, @startBanner) return end end if options.xiconfig.length < 1 then STDOUT.print "Enter XILog configuration [optional]: " options.xiconfig = STDIN.gets.chomp if options.xiconfig.length < 1 then options.xiconfig = self.getHardwareProfileDescription end end xilogger = XILogger.new(options.xiowner, options.xiconfig, self) end @toolCommand = RunTestLoopCommand.new(self, xilogger, selGroups, selTests, @startBanner) end #---------------------------------------- def runCommand self.parseOptions if @toolCommand == nil then self.showError() end STDOUT.puts "" return false unless @toolCommand.canRun? return @toolCommand.doToolCommand end #---------------------------------------- def doTestLoopSetup # called from RunTestLoopCommand.doToolCommand # override this method to perform any pre-test-loop actions return File.exists?(@logDir) end #---------------------------------------- def doTestLoopCleanup # called from RunTestLoopCommand.doToolCommand # override this method for special post-test actions return true end #---------------------------------------- def doRestoreSettings # called from RestoreSettingsCommand.doToolCommand # override this method to restore default # settings for this service return true end #---------------------------------------- def debugMsg(aMsg, methName) STDOUT.puts "DEBUG: #{aMsg} \<#{self.class.name}.#{methName}\>" if @debugMode end end #------------------------------------------------------------ # QLToolCommand - abstract class for top-level commands # # This class defines the purpose of the current invocation # of QuickLookTool based on the options and arguments # specified in the command line. # # instance variables # debugMode true = run tool with simulated responses # (no actual commands are executed) #------------------------------------------------------------ class QLToolCommand #---------------------------------------- def initialize(qltool) @qltool = qltool end #---------------------------------------- def qltool return @qltool end #---------------------------------------- def canRun? return true end #---------------------------------------- def doToolCommand # based class provided no default implementation, # derived classes must override return true end #---------------------------------------- def debugMsg(aMsg, methName) STDOUT.puts "DEBUG: #{aMsg} \<#{self.class.name}.#{methName}\>" if @qltool.debugMode end end #------------------------------------------------------------ # ShowVersionCommand - display the program version info #------------------------------------------------------------ class ShowVersionCommand < QLToolCommand #---------------------------------------- def initialize(qltool, versBanner) super(qltool) @versBanner = versBanner end #---------------------------------------- def doToolCommand STDOUT.puts "#{@versBanner} v#{@qltool.toolVersion} " + "(QuickLookTool v#{QuickLookTool::TOOL_VERSION})" STDOUT.puts "" return true end end #------------------------------------------------------------ # ShowHelpCommand - display the program help text #------------------------------------------------------------ class ShowHelpCommand < ShowVersionCommand #---------------------------------------- def initialize(qltool, helpText, versBanner) super(qltool, versBanner) @helpText = helpText end #---------------------------------------- def doToolCommand super unless @helpText == nil STDOUT.puts @helpText unless @helpText == nil STDOUT.puts "Use '#{@qltool.toolName} --help' to see" + " all available options." if @helpText == nil return true end end #------------------------------------------------------------ # ShowListCommand - displays the list of available test cases #------------------------------------------------------------ class ShowListCommand < QLToolCommand #---------------------------------------- def initialize(qltool, selGroups, selTests) super(qltool) @selGroups = selGroups @selTests = selTests end #---------------------------------------- def doToolCommand STDOUT.puts "Test cases:" STDOUT.puts "" cases = self.selectTestCases if cases.size < 1 then STDOUT.puts "No matching test cases found." else format = "%-12s %-32s %s\n" STDOUT.printf format, "Group", "Test Name", "Description" STDOUT.printf format, "-----", "---------", "-----------" cases.each do |tc| aTest = tc.new(0, @qltool) ti = aTest.getInfo STDOUT.printf format, ti[0], ti[1], ti[2] end end return true end #---------------------------------------- def selectTestCases # Creates filtered list of tests based on the selected # groups or selected tests command line options selCases = [] if @selGroups != nil || @selTests != nil then @qltool.logAndShowItems("* Selected test group(s): ", @selGroups) if @selGroups != nil @qltool.logAndShowItems("* Selected test case(s): ", @selTests) if @selTests != nil @qltool.testCases.each do |tc| aTest = tc.new(0, qltool) selected = false if @selGroups != nil then @selGroups.each do |sg| selected = sg == aTest.getGroup break if selected end elsif @selTests != nil @selTests.each do |st| selected = st == aTest.class.name break if selected end else selected = true end next unless selected selCases << tc end else STDOUT.puts "* Selected test cases: \" selCases = @qltool.testCases end return selCases end end #------------------------------------------------------------ # RestoreSettingsCommand - restores the service default settings #------------------------------------------------------------ class RestoreSettingsCommand < QLToolCommand #---------------------------------------- def canRun? return @qltool.isUserRoot? end #---------------------------------------- def doToolCommand @qltool.logAndShow("Restoring default settings for #{@qltool.serviceDesc}") return @qltool.doRestoreSettings end end #------------------------------------------------------------ # InstallBridgeCommand - installs the Ruby bridgesupport # for the selected framework #------------------------------------------------------------ class InstallBridgeCommand < QLToolCommand #---------------------------------------- def initialize(qltool, fwname) super(qltool) @fwname = fwname end #---------------------------------------- def canRun? return @qltool.isUserRoot? end #---------------------------------------- def doToolCommand fw="#{@fwname}.framework" STDOUT.puts "Checking for required #{fw}" self.debugMsg("skipping installation", "doToolCommand") STDOUT.puts "" return true if @qltool.debugMode bsFile="#{@fwname}.bridgesupport" bsDir="BridgeSupport" fwBase="/AppleInternal/Library/Frameworks" fwPath="#{fwBase}/#{fw}" fwPathRel="#{fw}/Resources" bdPathRel="#{fwPathRel}/BridgeSupport" bfPathRel="#{bdPathRel}/#{bsFile}" fwPathAbs="#{fwBase}/#{fwPathRel}" bdPathAbs="#{fwBase}/#{bdPathRel}" bfPathAbs="#{fwBase}/#{bfPathRel}" if !File.exists?("#{fwPath}") then STDOUT.puts "#{fwname}.framework not installed, exiting" return end if File.exists?("#{bfPathAbs}") then STDOUT.puts "#{fw} bridge support already installed, no action taken" return false end STDOUT.puts "Processing #{fw}.." if !File.exists?("#{bdPathAbs}") then STDOUT.puts "..creating directory: ../#{bdPathRel}" mkdirCmd = "/bin/mkdir -p \"#{bdPathAbs}\"" if @qltool.debugMode then self.debugMsg("cmd = \"#{mkdirCmd}\"", "doToolCommand") else `#{mkdirCmd}` end if !@qltool.debugMode && !File.exists?("#{bdPathAbs}") then STDOUT.puts "\n\n** Error -- failed to create \"#{bdPathAbs}\", exiting" return false end end STDOUT.puts "..creating metadata: ../#{bfPathRel}" genCmd = "/usr/bin/gen_bridge_metadata --framework \"#{fwPath}\" --output \"#{bfPathAbs}\"" if @qltool.debugMode then self.debugMsg("cmd = \"#{genCmd}\"", "doToolCommand") else `#{genCmd}` end if !@qltool.debugMode && !File.exists?("#{bfPathAbs}") then STDOUT.puts "** Error -- #{fw} bridge support installation failed." else STDOUT.puts "#{fw} bridge support successfully installed." end return true end end #------------------------------------------------------------ # RunTestLoopCommand - executes the test loop for selected tests #------------------------------------------------------------ class RunTestLoopCommand < ShowListCommand #---------------------------------------- def initialize(qltool, xilogger, selGroups, selTests, startBanner) super(qltool, selGroups, selTests) @xilogger = xilogger @startBanner = startBanner @numEnabled = 0 @numPassed = 0 end #---------------------------------------- def xilogger return @xilogger end #---------------------------------------- def canRun? return false unless @qltool.isUserRoot? return false unless @xilogger != nil ? @xilogger.isInstalled? : true return true if @qltool.debugMode # verify optional test pre-req's prf = @qltool.prereqFiles return true if prf == nil prf.each do |fpath| next if File.exists?(fpath) @qltool.showError("#{fpath} not installed! -- aborting") return false end return true end #---------------------------------------- def doToolCommand STDOUT.puts "\n * * * DEBUG MODE ENABLED (all tests simulated) * * *\n" if @qltool.debugMode @qltool.createTestDir @qltool.createLogFile self.showStartBanner @qltool.logAndShow("* XILog enabled : #{@xilogger != nil ? "YES" : "NO"}") if @xilogger != nil then @qltool.logAndShow("* XILog tool owner : \"#{@xilogger.logOwner}\"") @qltool.logAndShow("* XILog config : \"#{@xilogger.logConfig}\"") end @xilogger.doOpen unless @xilogger == nil # hook for derived class custom setup @qltool.logAndShow("* Initializing test environment") return false unless @qltool.doTestLoopSetup selCases = self.selectTestCases if selCases == nil || selCases.length < 1 then @qltool.logAndShow("No matching test cases for requests groups/tests.") return true end # # main test loop # @numEnabled = selCases.length seqNum = 0 selCases.each do |testClass| seqNum += 1 aTest = testClass.new(seqNum, qltool) if !aTest.doTest then @qltool.showError("one or more test failed!") break end @numPassed += 1 end # hook for derived class custom cleanup testOk = @qltool.doTestLoopCleanup @xilogger.doClose unless @xilogger == nil self.showEndBanner return testOk end #---------------------------------------- def showStartBanner @qltool.logAndShow("") @qltool.logAndShow(QuickLookTool::BANNER_BORDER) @qltool.logAndShow("#{@startBanner} v#{@qltool.toolVersion}" + " (QuickLookTool v#{QuickLookTool::TOOL_VERSION})") @qltool.logAndShow(QuickLookTool::BANNER_BORDER) end #---------------------------------------- def showEndBanner percent = ( @numPassed * 100 ) / @numEnabled passedMsg = "Passed #{@numPassed} / #{@numEnabled} tests (#{percent}%)." endTitle = "ALL TESTS COMPLETED" # print summary to console and log STDOUT.puts "" @qltool.logAndShow(QuickLookTool::BANNER_BORDER) STDOUT.puts "#{@qltool.getTime} #{endTitle}" @qltool.logMessage(endTitle) @qltool.logAndShow(passedMsg) STDOUT.puts "" STDOUT.puts "All test results logged in: #{@qltool.testLogPath}" @qltool.logAndShow("XILog output file: #{@xilogger.logOutPath}") unless @xilogger == nil @qltool.logAndShow(QuickLookTool::BANNER_BORDER) end end #------------------------------------------------------------ # QLTestCase - abstract base class for quicklook test cases # # QLTestCase defines the methods and data for a single # quicklook test case. # # instance variables # seq test number # qltool QuickLookTool instance for utility methods # desc test description # qlcmd QLCommand instance #------------------------------------------------------------ class QLTestCase #---------------------------------------- def initialize(seq, qltool, group, desc, qlcmd) @seq = seq @qltool = qltool @group = group @name = self.class.name @desc = desc @qlcmd = qlcmd end #---------------------------------------- def getTool return @qltool end #---------------------------------------- def getGroup return @group end #---------------------------------------- def getInfo return [@group, @name, @desc] end #---------------------------------------- def doTest testTitle = "[#{@seq}] #{@group}.#{@name}" testDesc = " [#{@seq}]:" descMsg = "TEST: #{testTitle}" STDOUT.print "\n#{@qltool.getTime} #{descMsg}" STDOUT.puts "" if @qltool.debugMode @qltool.logMessage("***") @qltool.logMessage(descMsg) @qltool.logMessage("#{testDesc} starting test") tc = @qltool.toolCommand xl = tc.xilogger xl.doBeginTest("#{@group}.#{@name}", @desc) unless xl == nil # execute the selected command @qlcmd.executeCommand(testDesc) # verify command results @qltool.logMessage("#{testDesc} checking results") testOk = @qlcmd.checkResults # log test results if testOk then STDOUT.puts " [PASSED]" @qltool.logMessage("#{testDesc} PASSED") # # report XILog success here # else # Log failed results border = "----------------------------------------" title = "RESULTS: epected vs. actual:" diffOut = @qlcmd.diffOutput # output results to the console STDOUT.puts(" [FAILED]", "") STDOUT.puts border STDOUT.puts title STDOUT.puts(diffOut) STDOUT.puts border STDOUT.puts "" # output results to the test log @qltool.logMessage("#{testDesc} FAILED") @qltool.logMessage(border) @qltool.logMessage(title) @qltool.logMessage(diffOut) @qltool.logMessage(border) # # report XILog failure here # xl.doLogError("TEST FAILED: \n#{title}\n#{diffOut}") unless xl == nil end xl.doEndTest(testOk) unless xl == nil return testOk end #---------------------------------------- def debugMsg(aMsg, methName) STDOUT.puts "DEBUG: #{aMsg} \<#{self.class.name}.#{methName}\>" if @qltool.debugMode end end #------------------------------------------------------------ # QLCommand # # Class defining command parameters for QLTest #------------------------------------------------------------ class QLCommand AUTO_REDIRECT = false MANUAL_REDIRECT = true #---------------------------------------- # initialize args # qltool QuickLookTool reference # command shell command line # expected expected command output # redir true = command controls output redirection # false = use standard output redirection def initialize(qltool, command, expected, redir = AUTO_REDIRECT) @qltool = qltool @command = command @expected = expected @redir = redir td = @qltool.logDir @expectedOutPath = "#{td}/expected.out" @expectedErrPath = "#{td}/expected.err" @commandOutPath = "#{td}/command.out" @commandErrPath = "#{td}/command.err" @diffOutPath = "#{td}/diff.out" end #---------------------------------------- def qltool return @qltool end #---------------------------------------- def command return @command end #---------------------------------------- def expected return @expected end #---------------------------------------- def redir return @redir end #---------------------------------------- def diffOutput data = @qltool.readFromFile(@diffOutPath) self.debugMsg("file not found: #{@diffOutPath}", "diffOutput") if data == nil return data != nil ? data : "" end #---------------------------------------- def saveErrorOutputFiles system("cp", @expectedOutPath, @expectedErrPath) system("cp", @commandOutPath, @commandErrPath) end #---------------------------------------- def executeCommand(desc) # remove any old results @qltool.removeFiles([ @expectedOutPath, @expectedErrPath, @commandOutPath, @commandErrPath, @diffOutPath ]) # set the expected results @qltool.writeToFile(@expectedOutPath, @expected) # run the command, directing output to actual results if qltool.debugMode then aMsg = "command(D) = \"#{@command}\"" self.debugMsg(aMsg, "executeCommand") @qltool.logMessage("#{desc} #{aMsg}") @qltool.writeToFile(@commandOutPath, @expected) elsif @redir == AUTO_REDIRECT then # automatic output redirection @qltool.logMessage("#{desc} command(A) = \"#{@command}\"") cmdOut = `#{@command}` @qltool.writeToFile(@commandOutPath, cmdOut) else # manual output redirection cmd = "#{@command} > \"#{@commandOutPath}\"" @qltool.logMessage("#{desc} command(M) = \"#{cmd}\"") system(cmd) end end #---------------------------------------- def checkResults # Compare the command output with the expected # output and record success or failure. In case # of failure, the expected and actual output are # saved to prevent overwrite before the command # results are recorded in the log. `diff #{@expectedOutPath} #{@commandOutPath} > #{@diffOutPath}` testOk = File.size(@diffOutPath) == 0 self.saveErrorOutputFiles unless testOk return testOk end #---------------------------------------- def debugMsg(aMsg, methName) STDOUT.puts "DEBUG: #{aMsg} \<#{self.class.name}.#{methName}\>" if @qltool.debugMode end end #------------------------------------------------------------ # SACommand # # Specialized QLCommand sub-class for serveradmin commands #------------------------------------------------------------ class SACommand < QLCommand SA_TOOL = "/usr/sbin/serveradmin" #---------------------------------------- # initialize args # qltool QuickLookTool reference # func serveradmin function # arg function arguments if needed # results expected output from command def initialize(qltool, func, args, results) cmd = "#{func} #{qltool.serviceID}" cmd = "#{cmd}:#{args}" if args != nil ev = results != nil ? results : "" super(qltool, "#{SA_TOOL} #{cmd}", "#{ev}") end #---------------------------------------- def stopService # Stop the service outside the test enviroment self.setServiceState("stop") end #---------------------------------------- def startService # Starting the service outside the test enviroment self.setServiceState("start") end #---------------------------------------- def setServiceState(aVal) # Change the service state outside the test enviroment stateCmd = "#{SA_TOOL} #{aVal} #{@qltool.serviceID}" self.debugMsg("cmd = \"#{stateCmd}\"", "setServiceState") `#{stateCmd}` unless @qltool.debugMode end #---------------------------------------- def readSetting(aKey, debugVal) # Read a service setting value outside the test enviroment rsCmd = "#{SA_TOOL} settings #{@qltool.serviceID} | grep #{aKey} | awk '{print $3}'" self.debugMsg("cmd = #{rsCmd}", "readSetting") return "#{@qltool.serviceID}:#{aKey} = #{debugVal}" if @qltool.debugMode return `#{rsCmd}`.chomp! end #---------------------------------------- def writeSetting(aKey, aVal) # Write a service setting value outside the test enviroment wsCmd = "#{SA_TOOL} settings #{@qltool.serviceID}:#{aKey} = #{aVal}" self.debugMsg("cmd = \"#{wsCmd}\"", "writeSetting") `#{wsCmd}` unless @qltool.debugMode end end #------------------------------------------------------------ # SAStateCommand # # SACommand variant for setting service state #------------------------------------------------------------ class SAStateCommand < SACommand STATE_STOP = "stop" STATE_START = "start" #---------------------------------------- # initialize args # qltool QuickLookTool reference # state new service state def initialize(qltool, state) ev = state == STATE_START ? "RUNNING" : "STOPPED" super(qltool, state, nil, "#{qltool.serviceID}:state = \"#{ev}\"") end end #------------------------------------------------------------ # SASettingsCommand # # SACommand variant for modifying service settings #------------------------------------------------------------ class SASettingsCommand < SACommand #---------------------------------------- # initialize args # qltool QuickLookTool reference # key settings key # value value to write for key # expVal if non-nil, use this value for # verifying the command result def initialize(qltool, key, value, expVal = nil) serviceID = qltool.serviceID ev = expVal != nil ? expVal : value super(qltool, "settings", "#{key} = #{value}", "#{serviceID}:#{key} = #{ev}") end #---------------------------------------- def executeCommand(desc) # SASettingsCommand overrides the base class method # to bracket command execution with a service recycle # in order to activate the new setting @qltool.logMessage("#{desc} stopping service: #{@qltool.serviceDesc}") self.stopService super(desc) @qltool.logMessage("#{desc} re-starting service: #{@qltool.serviceDesc}") self.startService end end #------------------------------------------------------------ # XILogger # # Wrapper class for XILog RubyCocoa bridge. QuickLookTool # creates an instance of this class if the --xilog option # is selected. #------------------------------------------------------------ class XILogger XILOG_XML = true XILOG_ECHO = false XILOG_FRAMEWORK = "/AppleInternal/Library/Frameworks/XILog.framework" XILOG_BRIDGESUPPORT = "#{XILOG_FRAMEWORK}/Resources/BridgeSupport/XILog.bridgesupport" #---------------------------------------- def initialize(owner, config, qltool) @logOwner = owner @logConfig = config @qltool = qltool @logOutPath = "#{qltool.logDir}/xiLog.log" @logRef = nil require 'osx/cocoa' unless @qltool.debugMode #$: << "/AppleInternal/Library/Ruby" #require 'XILog' end #---------------------------------------- def logOwner return @logOwner end def logConfig return @logConfig end def logOutPath return @logOutPath end #---------------------------------------- def isInstalled? return true if @qltool.debugMode [XILOG_FRAMEWORK, XILOG_BRIDGESUPPORT].each do |fpath| #[XILOG_FRAMEWORK].each do |fpath| next if File.exists?(fpath) if fpath == XILOG_FRAMEWORK then @qltool.showError("XILog.framework is not installed.") else @qltool.showError("Missing BridgeSupport for XILog.framework.") STDOUT.puts "Hint: use \'#{@qltool.toolName} --install\' to create missing BridgeSupport file." end return false end return true end #---------------------------------------- def doOpen if @qltool.debugMode then self.debugMsg("cmd = XILogOpenLog(\"#{@logOutPath}\", \"#{@qltool.toolName}\", \"#{@logOwner}\", \"#{@logConfig}\")", "doOpen") #self.debugMsg("cmd = XILog.open(\"#{@logOutPath}\", \"#{@qltool.toolName}\", \"#{@logOwner}\", \"#{@logConfig}\")", "doOpen") return end OSX.require_framework XILOG_FRAMEWORK @logRef = OSX.XILogOpenLog(@logOutPath, @qltool.toolName, @logOwner, @logConfig, XILOG_XML, XILOG_ECHO) #@logRef = XIlog::open(@logOutPath, @qltool.toolName, @logOwner, @logConfig, XILOG_XML, XILOG_ECHO) if @logRef == nil then errMsg = "WARNING: XILogOpenLog failed; continuing test with XILog disabled" @qltool.showError(errMsg) @qltool.logMessage(errMsg) end end #---------------------------------------- def doClose if @qltool.debugMode then self.debugMsg("cmd = XILogCloseLog()", "doClose") #self.debugMsg("cmd = XILog.close", "doClose") return end OSX.XILogCloseLog(@logRef) #@logRef.close end #---------------------------------------- def doBeginTest(name, desc) if @qltool.debugMode then self.debugMsg("cmd = XILogBeginTestCase(\"#{name}\", \"#{desc}\")", "doBeginTest") #self.debugMsg("cmd = XILog.begin_test(\"#{name}\", \"#{desc}\")", "doBeginTest") return end OSX.XILogBeginTestCase(@logRef, name, desc) #@logRef.begin_test(name, desc) end #---------------------------------------- def doEndTest(testOk) if @qltool.debugMode then self.debugMsg("cmd = XILogEndTestCase(#{testOk.to_s})", "doEndTest") #self.debugMsg("cmd = XILog.end_test", "doEndTest") return end OSX.XILogEndTestCase(@logRef, testOk ? OSX::KXILogTestPass : OSX::KXILogTestFail) #@logRef.end_test end #---------------------------------------- def doLogMessage(msg) if @qltool.debugMode then self.debugMsg("cmd = XILogMessage(\"#{msg})\"", "doLogMessage") #self.debugMsg("cmd = XILog.log(\"#{msg})\"", "doLogMessage") return end OSX.XILogMessage(@logRef, msg) #@logRef.log(msg) end #---------------------------------------- def doLogError(msg) if @qltool.debugMode then self.debugMsg("cmd = XILogError(\"#{msg}\")", "doLogError") #self.debugMsg("cmd = XILog.error(\"#{msg}\", XILog::ERROR)", "doLogMessage") return end OSX.XILogError(@logRef, msg) #@logRef.error(msg, XILog::ERROR) end #---------------------------------------- def debugMsg(aMsg, methName) STDOUT.puts "\nDEBUG: #{aMsg} \<#{self.class.name}.#{methName}\>" if @qltool.debugMode end end