require 'getoptlong' require 'Logger' require 'singleton' require 'fileutils' require 'net/ftp' require 'net/smtp' require 'iconv' require 'erb' include ERB::Util # Setting up RAGE_DIR = ENV['RAGE_DIR'].gsub(/\\/, "/") RS_CODEBRANCH = ENV['RS_CODEBRANCH'].gsub(/\\/, "/") RS_BUILDBRANCH = ENV['RS_BUILDBRANCH'].gsub(/\\/, "/") RS_TOOLSROOT = ENV['RS_TOOLSROOT'].gsub(/\\/, "/") RS_TITLE = ENV['RS_TITLE'] USER_PROFILE = ENV['USERPROFILE'] USER_DESKTOP = USER_PROFILE + "/Desktop" USERNAME = ENV['USERNAME'] USER_LOG_PATH = USER_PROFILE + "/Documents/Rockstar Games/GTA V" GAME = "GTAV" PROTECTION_PATH = RS_TOOLSROOT + "/script/coding/protection" TOOL_PATH = PROTECTION_PATH + "/launcherTester" GENERATED_PATH = PROTECTION_PATH + "/generated" CURR_TIME = Time.now.strftime("%Y%m%d.%H.%M.%S") LOG_SEVERITY_LEVEL = Logger::INFO DEFAULT_RELEASE = "" DEFAULT_SKIP_BUILDS = false DEFAULT_USERNAME = "" DEFAULT_PASSWORD = "" PATCHBUILDER_DIR = RS_CODEBRANCH + "/extra/patchbuilder" PATCHBUILDER_DATA_DIR = PATCHBUILDER_DIR + "/Data/" PATCHBUILDER_OUTPUT_DIR = PATCHBUILDER_DIR + "/Output/" GAME_DIR = "C:/Program Files/Rockstar Games/Grand Theft Auto V/" OLD_PATCH = "1.0.0.0" OLD_PATCH_REGEX = "1([._,])0([._,])0([._,])0" NEW_PATCH_UNDERSCORE = "1_0_0_1" NEW_PATCH_REGEX = "1\\10\\10\\11" OLD_XML_FILE = "launcher_version.xml" NEW_XML_FILE = "launcher_version.testing.#{USERNAME}.xml" PROGFILESX86 = ENV['ProgramFiles(x86)'].gsub(/\\/, "/") SIGNTOOL_DIR = PROGFILESX86 + "/Windows Kits/8.0/bin/x86/" $LOG_DIR = "C:/" $SHOW_ERROR_DELAY = 500 # Clearing out our data directory, as we don't need it any more. We do this ahead of the creation, so the directory will be there for when we need to write to it. FileUtils.rm_rf("#{PATCHBUILDER_DATA_DIR}") # Then our generated paths def safecreatepath(path) begin FileUtils.mkdir_p(path) unless File.exists?(path) rescue Exception =>e puts e.message end end safecreatepath(GENERATED_PATH) safecreatepath(PATCHBUILDER_OUTPUT_DIR) safecreatepath(PATCHBUILDER_DATA_DIR) safecreatepath(PROTECTION_PATH) class Parameters attr_reader :release attr_reader :skipbuilds attr_reader :skipupload attr_reader :username attr_reader :password attr_reader :emailfrom attr_reader :emailto attr_reader :yes attr_reader :showerror attr_reader :errorsonly attr_reader :noerrors attr_reader :preloader def initialize @release = DEFAULT_RELEASE #nil @skipbuilds = DEFAULT_SKIP_BUILDS @skipupload = DEFAULT_SKIP_BUILDS @username = DEFAULT_USERNAME @password = DEFAULT_PASSWORD @emailfrom = "" @emailto = "" @yes = false @showerror = false @errorsonly = false @noerrors = false @preloader = false @opts = GetoptLong.new( [ '--release', '-r', GetoptLong::REQUIRED_ARGUMENT ], [ '--username', '-u', GetoptLong::REQUIRED_ARGUMENT ], [ '--password', '-p', GetoptLong::REQUIRED_ARGUMENT ], [ '--skipbuild', '-s', GetoptLong::NO_ARGUMENT ], [ '--skipupload', '-k', GetoptLong::NO_ARGUMENT ], [ '--emailfrom', '-e', GetoptLong::REQUIRED_ARGUMENT ], [ '--emailto', '-t', GetoptLong::REQUIRED_ARGUMENT ], [ '--always-yes', '-y', GetoptLong::NO_ARGUMENT ], [ '--showerror', '-o', GetoptLong::NO_ARGUMENT ], [ '--errorsonly', '-z', GetoptLong::NO_ARGUMENT ], [ '--noerrors', '-x', GetoptLong::NO_ARGUMENT ], [ '--logdir', '-l', GetoptLong::REQUIRED_ARGUMENT ], [ '--errordelay', '-d', GetoptLong::REQUIRED_ARGUMENT ], [ '--preloader', '-h', GetoptLong::NO_ARGUMENT ] ) end def PrintHelp() RsgLog.instance().info("DNE Yet"); end def ParseArguments() @opts.each do |opt, arg| case opt when '--help' PrintHelp() exit when '--skipbuild' @skipbuilds = true when '--skipupload' @skipupload = true when '--always-yes' @yes = true; when '--username' @username = arg when '--password' @password = arg when '--emailfrom' @emailfrom = arg when '--emailto' @emailto = arg when '--showerror' @showerror = true when '--errorsonly' @errorsonly = true when '--noerrors' @noerrors = true when '--preloader' @preloader = true when '--logdir' $LOG_DIR = arg when '--errordelay' $SHOW_ERROR_DELAY = arg when '--release' case arg when /release/i,/bankrelease/i,/debug/i,/final/i,/beta/i, /master/i @release = arg else RsgLog.instance().error("Invalid release [#{arg}] specified") exit end end end end end class MultiDelegator def initialize(*targets) @targets = targets end def self.delegate(*methods) methods.each do |m| define_method(m) do |*args| @targets.map { |t| t.send(m, *args) } end end self end class < log = RsgLog.instance() # Banner log.info("=======================") log.info("Launcher Testing Script") log.info("=======================") log.info("Parsing arguments...") parameters = Parameters.new parameters.ParseArguments() if !parameters.skipupload && !parameters.showerror && !parameters.errorsonly if !parameters.yes log.warn("THIS SCRIPT REVERTS AND MODIFIES YOUR LAUNCHER_CONFIG.XML FILE. PRESS ANY KEY TO CONTINUE.") system("pause") end if parameters.release == "" parameters.PrintHelp() log.error("Invalid release [#{parameters.release}] specified") exit end if parameters.username == "" parameters.PrintHelp() log.error("Invalid username [#{parameters.username}] specified") exit end if parameters.password == "" parameters.PrintHelp() log.error("Invalid password [#{parameters.password}] specified") exit end # Revert and edit the Bootstrap log.info("Checking out files in Perforce...") LauncherTestPerforceHandler.instance().Revert() LauncherTestPerforceHandler.instance().Edit() # Good, now the files we care about are in a pristine state (or should be) if parameters.skipbuilds == false # Now lets swap out the tokens in our XML file so we point to the right patch file on the server ReplaceTokenInFile("#{RS_CODEBRANCH}\\extra\\launcher\\Launcher\\res\\launcher_config.xml", OLD_XML_FILE, NEW_XML_FILE) # Lets build the first version of the Bootstrap log.info("Building first version of the Bootstrap..."); ProcessConfiguration("#{RS_CODEBRANCH}\\extra\\launcher\\Bootstrap.sln", parameters.release) log.info("Protecting first version of the Bootstrap...") ProtectIT("bootstrap", parameters.release, parameters.preloader) firstProtectedBootstrap = GetMostRecentFile() log.info("First Protected Bootstrap #{firstProtectedBootstrap}") # Increment the version number # Since Ruby 1.8.7 has absolute shit support for UTF-16 search/replacing, I'm just copying over a new file # Yes it's a hack. I've spent 2 hours trying to do it the proper way. I give up. FileUtils.cp("#{TOOL_PATH}/patched/Bootstrap.rc", "#{RS_CODEBRANCH}/extra/launcher/Bootstrap/Bootstrap.rc") # Build the second version of the bootstrap (the one we're patching down log.info("Building second version of the Bootstrap..."); ProcessConfiguration("#{RS_CODEBRANCH}\\extra\\launcher\\Bootstrap.sln", parameters.release) log.info("Protecting second version of the Bootstrap...") ProtectIT("bootstrap", parameters.release,parameters.preloader) secondProtectedBootstrap = GetMostRecentFile() log.info("Second Protected Bootstrap #{secondProtectedBootstrap}") # Do the same thing with the Launcher log.info("Building first version of the Launcher..."); ProcessConfiguration("#{RS_CODEBRANCH}\\extra\\launcher\\Launcher.sln", parameters.release) log.info("Protecting first version of the Launcher...") ProtectIT("launcher", parameters.release,parameters.preloader) firstProtectedLauncher = GetMostRecentFile() log.info("First Protected Launcher #{firstProtectedLauncher}") # Increment the Version # FileUtils.cp("#{TOOL_PATH}/patched/Launcher.rc", "#{RS_CODEBRANCH}/extra/launcher/Launcher/Launcher.rc") # Second version of the launcher log.info("Building second version of the Launcher..."); ProcessConfiguration("#{RS_CODEBRANCH}\\extra\\launcher\\Launcher.sln", parameters.release) log.info("Protecting second version of the Launcher...") ProtectIT("launcher", parameters.release,parameters.preloader) secondProtectedLauncher = GetMostRecentFile() log.info("Second Protected Launcher #{secondProtectedLauncher}") # Now we have to do the same with RGSC. #stellar. # The worst part is, it requires interaction with the user # The good part is, all I need to do is call the collect_ script. # Lets create the first protected version of RGSC # Create the second protected version of RGSC end # Create the patches # First lets create the bootstrap patch # We do this by copying the second bootstrap into the data directory of the patchbuilder log.info("Building the installable patch executables...") # First clear out the directory FileUtils.rm_rf("#{PATCHBUILDER_DATA_DIR}/*") # Copy the file over FileUtils.cp(secondProtectedBootstrap, PATCHBUILDER_DATA_DIR + "/PlayGTAV.exe") # Now we run the CreatePatchTool to build the patch system("#{RS_CODEBRANCH}/extra/patchbuilder/CreatePatchTest.bat Prelauncher #{NEW_PATCH_UNDERSCORE} > #{GENERATED_PATH}/#{CURR_TIME}.bootstrap.patchbuilder.log") # First clear out the directory FileUtils.rm_rf("#{PATCHBUILDER_DATA_DIR}/*") # Copy the file over FileUtils.cp(secondProtectedLauncher, PATCHBUILDER_DATA_DIR + "/GTAVLauncher.exe") # Now we run the CreatePatchTool to build the patch system("#{RS_CODEBRANCH}/extra/patchbuilder/CreatePatchTest.bat Launcher #{NEW_PATCH_UNDERSCORE} > #{GENERATED_PATH}/#{CURR_TIME}.launcher.patchbuilder.log") sleep(3) # Create our FTP pipe log.info("Creating our FTP connection...") ftp = Net::FTP.new("rsgames.upload.llnw.net") ftp.login(parameters.username, parameters.password) # Upload the patches to the FTP log.info("Uploading the patches...") ftp.chdir('/patches/dev/gtav/Launcher_EFIGS') Dir.glob("#{PATCHBUILDER_OUTPUT_DIR}/*.exe").each do |patch| # Sign the file before we do anything with it log.info("Signing #{patch}...") SignFile(patch) # Now upload it to the server log.info("Uploading #{patch}...") ftp.putbinaryfile(patch) end # Upload the XML files to the FTP log.info("Uploading new XML files...") ftp.chdir('/patches/dev/gtav') # Remove old files - hitting errors where they are read-only begin FileUtils.rm_f("#{GENERATED_PATH}/launcher_version.testing.#{USERNAME}.xml") rescue end begin FileUtils.rm_f("#{GENERATED_PATH}/prelauncher_version.testing.#{USERNAME}.xml") rescue end FileUtils.cp("#{TOOL_PATH}/patched/launcher_version.testing.xml", "#{GENERATED_PATH}/launcher_version.testing.#{USERNAME}.xml") FileUtils.cp("#{TOOL_PATH}/patched/prelauncher_version.testing.xml", "#{GENERATED_PATH}/prelauncher_version.testing.#{USERNAME}.xml") ftp.putbinaryfile("#{GENERATED_PATH}/launcher_version.testing.#{USERNAME}.xml") ftp.putbinaryfile("#{GENERATED_PATH}/prelauncher_version.testing.#{USERNAME}.xml") # Copy over the first iterations to our build directory log.info("Copying executables over to the build directory...") if parameters.skipbuilds == false FileUtils.cp(firstProtectedBootstrap, GAME_DIR + "/PlayGTAV.exe") FileUtils.cp(firstProtectedLauncher, GAME_DIR + "/GTAVLauncher.exe") FileUtils.cp(firstProtectedBootstrap, RS_BUILDBRANCH + "/PlayGTAV.exe") FileUtils.cp(firstProtectedLauncher, RS_BUILDBRANCH + "/GTAVLauncher.exe") end # Deleting a local file to test the downloading of pipes log.info("Deleting a local RPF file...") begin FileUtils.rm("#{GAME_DIR}/x64a.rpf") rescue end # Deleting game executable to test the downloading of patches log.info("Deleting a local RPF file...") begin FileUtils.rm("#{GAME_DIR}/gta5.exe") rescue end # Revert back out the files we opened up log.info("Reverting files that we've opened up..."); LauncherTestPerforceHandler.instance().Revert() # Restore the files that we've previously clobbered # Close FTP connection log.info("Closing FTP connection...") ftp.close else log.info("Skipping build / upload.") DeleteSomeFiles() end # Defining the testing functions down here, to not get mixed up with the # patch-building / -uploading code. PASS_COLOUR = "#004400" FAIL_COLOUR = "#880000" INFO_COLOUR = "#202020" $currentLogfilePath = "" $currentLogfileDestPath = "" $currentLogFile = nil $reportHtml = "" $sectionTitle = "" $sectionBody = "" $sectionPassed = true $sections = [] def StartReport() $reportHtml = '' $reportHtml += "\n
\n

Launcher Automated Test Results

\n" $reportHtml += "
Summary
\n
    " $reportHtml += "
  • User: " + USERNAME + "
  • \n" $reportHtml += "
  • Userprofile: " + USER_PROFILE + "
  • \n" $reportHtml += "
  • Log dir: " + USER_LOG_PATH + "
  • \n" p4ver = "(unknown)" begin p4ver = `p4 changes -m 1 X:\\gta5\\src\\dev_ng\\extra\\launcher...#have` p4ver = p4ver.split(" ")[1] rescue end $reportHtml += "
  • CL: " + p4ver.to_s + "
  • \n" end def StartSection(title) RsgLog.instance().info("Starting section \"#{title}\"...") $sectionTitle = title $sectionBody = "" $sectionPassed = true $sectionBody += "\n" end def OpenLogFile(filename) RsgLog.instance().info("Opening log file \"#{filename}\"...") $currentLogfilePath = "#{USER_LOG_PATH}/#{filename}" begin $currentLogFile = File.open($currentLogfilePath) rescue $currentLogFile = nil end end def InsertLogFilePath() $sectionBody += "\n" end def CopyAndOpenLogFile(filename) $currentLogfilePath = "#{USER_LOG_PATH}/#{filename}" destPath = "#{$LOG_DIR}/" + Time.now.strftime("%Y%m%d.%H.%M.%S") + "." + filename #destPath = "#{$LOG_DIR}/#{CURR_TIME}.#{filename}" RsgLog.instance().info("Source log: "+$currentLogfilePath) RsgLog.instance().info("Dest log: "+destPath) FileUtils.cp($currentLogfilePath, destPath) $currentLogFile = File.open($currentLogfilePath) $currentLogfileDestPath = destPath InsertLogFilePath() end def ContinueLogFileSearch(string) line = nil pos = $currentLogFile.pos while (line = $currentLogFile.gets) != nil if (line.include?(string)) return true end end # Rewind on failure $currentLogFile.close $currentLogFile = File.open($currentLogfilePath) $currentLogFile.pos = pos return false end def ContinueLogFileSearchPassFail(string0, string1) line = nil pos = $currentLogFile.pos while (line = $currentLogFile.gets) != nil if (line.include?(string0)) return 0 end if (line.include?(string1)) # Rewind on failure $currentLogFile.pos = pos return 1 end end # Rewind on failure $currentLogFile.close $currentLogFile = File.open($currentLogfilePath) $currentLogFile.pos = pos return -1 end def ContinueLogFileSearchReturnLine(string0) line = nil pos = $currentLogFile.pos while (line = $currentLogFile.gets) != nil if (line.include?(string0)) return line end end # Rewind on failure $currentLogFile.close $currentLogFile = File.open($currentLogfilePath) $currentLogFile.pos = pos return nil end def ReadLineFromFile(file, linenumber) line = nil begin File.open(file) do |f| linenumber.times { line = f.gets } end rescue end return line end def AddResultLine(text, passed) if passed RsgLog.instance().info("#{text} - PASSED") $sectionBody += "\n" else RsgLog.instance().info("#{text} - FAILED") $sectionBody += "\n" $sectionPassed = false end end def AddInfoLine(text) RsgLog.instance().info("#{text} - INFO") $sectionBody += "\n" end def AddSmallInfoLine(text) RsgLog.instance().info("#{text} - INFO") $sectionBody += "\n" end def AddCodeLine(text) if text == nil return end text = html_escape(text) RsgLog.instance().info("#{text} - INFO") $sectionBody += '' $sectionBody += "\n" end def TestForLogLine(search) AddResultLine("Check for \"#{search}\"", ContinueLogFileSearch(search)) end def TestForLogPassFail(pass, fail) AddResultLine("Check for \"#{pass}\"", ContinueLogFileSearchPassFail(pass, fail) == 0) end def CloseLogFile() $currentLogFile.close end def Countdown(var) while var > 0 RsgLog.instance().info(var.to_s + "... ") sleep(1) var = var - 1 end end def EndSection() RsgLog.instance().info("Ending section...") section = "
    " + $sectionTitle if $sectionPassed section += " - PASSED
    \n" else section += " - FAILED
    \n" end section += $sectionBody section += "\n
    Logfile: #{$currentLogfileDestPath}[open]
    #{text}PASSED
    #{text}FAILED
    #{text}INFO
    #{text}INFO
    ' + text + 'INFO
\n
\n\n" $sections.push(section) $reportHtml += "
  • " + $sectionTitle if $sectionPassed $reportHtml += " - PASSED
  • \n" else $reportHtml += " - FAILED\n" end sleep(0.2) end def EndReport() $reportHtml += "
    \n
    \n" $sections.each {|i| $reportHtml += i } $reportHtml += "" end def BootstrapSelfPatchTest() random_arg = "-autotest_random_arg_" + rand(100).to_s RsgLog.instance().info("Launching Bootstrap only...") result = system(GAME_DIR + "/PlayGTAV.exe", "-autotest_bootstrap_nolauncher", random_arg) StartSection("Bootstrap Patch Check") AddResultLine("Execution success", result) AddResultLine("Process return code == 0", $? == 0) generated_log = false OpenLogFile("bootstrap.log") generated_log = ContinueLogFileSearch(random_arg) CloseLogFile() AddResultLine("Created log file", generated_log) AddResultLine("Generated a log file", generated_log) CopyAndOpenLogFile("bootstrap.log") if generated_log TestForLogLine("Bootstrap starting!") TestForLogLine("Bmp size: 573 x 601") TestForLogLine("There is/are 2 check(s) to do") TestForLogLine("Update check 1/2 starting") TestForLogPassFail("RockstarDownloader doing download.", "Setting error condition") TestForLogPassFail("RockstarDownloader checking URL validity...", "Setting error condition") TestForLogPassFail("RockstarDownloader checking disk...", "Setting error condition") TestForLogPassFail("prelauncher_version.testing.#{USERNAME}.xml to determine length...", "Setting error condition") TestForLogPassFail("Update check 1/2 done", "Update check 1/2 failed") TestForLogPassFail("Check 1 patch 1 is not installed; queueing.", "Check 1 patch 1 is installed.") TestForLogLine("Update check 2/2 starting") TestForLogPassFail("launcher_version.testing.#{USERNAME}.xml to determine length...", "Setting error condition") TestForLogPassFail("Update check 2/2 done", "Update check 2/2 failed") TestForLogPassFail("Check 2 patch 1 is not installed; queueing.", "Check 2 patch 1 is installed.") TestForLogLine("Found 2 patches.") TestForLogLine("Installing patch") TestForLogLine("Install needs restart") TestForLogLine("Installer requested restart") TestForLogLine("Restart required; exiting") end CloseLogFile() EndSection() end def BootstrapLauncherPatchTest() random_arg = "-autotest_random_arg_" + rand(100).to_s RsgLog.instance().info("Launching Bootstrap only...") result = system(GAME_DIR + "/PlayGTAV.exe", "-autotest_bootstrap_nolauncher", random_arg) StartSection("Launcher Patch Check") AddResultLine("Execution success", result) AddResultLine("Process return code == 0", $? == 0) generated_log = false OpenLogFile("bootstrap.log") generated_log = ContinueLogFileSearch(random_arg) CloseLogFile() AddResultLine("Generated a log file", generated_log) CopyAndOpenLogFile("bootstrap.log") if generated_log TestForLogLine("Bootstrap starting!") TestForLogLine("Bmp size: 573 x 601") TestForLogLine("There is/are 2 check(s) to do") TestForLogLine("Update check 1/2 starting") TestForLogPassFail("RockstarDownloader doing download.", "Setting error condition") TestForLogPassFail("RockstarDownloader checking URL validity...", "Setting error condition") TestForLogPassFail("RockstarDownloader checking disk...", "Setting error condition") TestForLogPassFail("prelauncher_version.testing.#{USERNAME}.xml to determine length...", "Setting error condition") TestForLogPassFail("Update check 1/2 done", "Update check 1/2 failed") TestForLogPassFail("Check 1 was up-to-date.", "Check 1 patch 1 is not installed; queueing.") TestForLogLine("Update check 2/2 starting") TestForLogPassFail("launcher_version.testing.#{USERNAME}.xml to determine length...", "Setting error condition") TestForLogPassFail("Update check 2/2 done", "Update check 2/2 failed") TestForLogPassFail("Check 2 patch 1 is not installed; queueing.", "Check 2 patch 1 is installed.") TestForLogLine("Found 1 patches.") TestForLogLine("Installing patch") TestForLogLine("About to launch process") TestForLogPassFail("Pid:", "Error launching process") TestForLogLine("Waiting for handle") TestForLogLine("Finished waiting") TestForLogPassFail("Successfully installed patch", "Failed to install patch") TestForLogLine("No restart required; chaining Launcher") end CloseLogFile() EndSection() end def BootstrapLauncherRunTest() random_arg = "-autotest_random_arg_" + rand(100).to_s RsgLog.instance().info("Launching Bootstrap (expecting Launcher as well)...") result = system(GAME_DIR + "/PlayGTAV.exe", "-autotest_launcher_exitimmediately", random_arg) StartSection("Bootstrap Starting Launcher") AddResultLine("Execution success", result) AddResultLine("Process return code == 0", $? == 0) OpenLogFile("bootstrap.log") generated_log = ContinueLogFileSearch(random_arg) CloseLogFile() AddResultLine("Bootstrap generated a log file", generated_log) CopyAndOpenLogFile("bootstrap.log") if generated_log TestForLogLine("Bootstrap starting!") TestForLogLine("Bmp size: 573 x 601") TestForLogLine("There is/are 2 check(s) to do") TestForLogLine("Update check 1/2 starting") TestForLogPassFail("RockstarDownloader doing download.", "Setting error condition") TestForLogPassFail("RockstarDownloader checking URL validity...", "Setting error condition") TestForLogPassFail("RockstarDownloader checking disk...", "Setting error condition") TestForLogPassFail("prelauncher_version.testing.#{USERNAME}.xml to determine length...", "Setting error condition") TestForLogPassFail("Update check 1/2 done", "Update check 1/2 failed") TestForLogPassFail("Check 1 was up-to-date.", "Check 1 patch 1 is not installed; queueing.") TestForLogLine("Update check 2/2 starting") TestForLogPassFail("launcher_version.testing.#{USERNAME}.xml to determine length...", "Setting error condition") TestForLogPassFail("Update check 2/2 done", "Update check 2/2 failed") TestForLogPassFail("Check 2 was up-to-date.", "Check 2 patch 1 is not installed; queueing.") TestForLogLine("Found 0 patches.") TestForLogLine("No restart required; chaining Launcher") end CloseLogFile() # Give it a moment sleep(3) OpenLogFile("launcher.log") generated_log = ContinueLogFileSearch(random_arg) CloseLogFile() AddResultLine("Launcher generated a log file", generated_log) CopyAndOpenLogFile("launcher.log") CloseLogFile() EndSection() end def LauncherRunTest() random_arg = "-autotest_random_arg_" + rand(100).to_s RsgLog.instance().info("Launching Launcher...") result = system(GAME_DIR + "/GTAVLauncher.exe", random_arg, "-autotest_launcher_selfsignin", "-autotest_launcher_digitalupdate") StartSection("Launcher Startup") AddResultLine("Execution success", result) AddResultLine("Process return code == 0", $? == 0) OpenLogFile("launcher.log") generated_log = ContinueLogFileSearch(random_arg) CloseLogFile() AddResultLine("Launcher generated a log file", generated_log) if !generated_log EndSection() return end CopyAndOpenLogFile("launcher.log") TestForLogLine("Running as admin") TestForLogLine("P4 Version") TestForLogLine("Waiting for connections on named pipe instance") TestForLogLine("Social Club constructed") TestForLogLine("Now on background") TestForLogLine("Hiding all controls") TestForLogLine("Found 0 in-file(s)") TestForLogLine("OS version") TestForLogLine("Controller monitor thread launched") EndSection() StartSection("Launcher Online Config") InsertLogFilePath() TestForLogLine("eDownloadOnlineConfig") TestForLogLine("JOB_DOWNLOAD_ONLINE_CONFIG") TestForLogLine("launcher_online_config.xml to determine length...") TestForLogPassFail("Content length", "error") TestForLogPassFail("Download succeeded", "nothing to download") EndSection() StartSection("Launcher RGSC Update") InsertLogFilePath() TestForLogPassFail("Entering state 2 (eCheckForRGSCUpdates)", "eCheckForInPlaceDownload") TestForLogLine("Dev server looks accessible") TestForLogPassFail("There is/are 1 check(s) to do.", "error") TestForLogPassFail("Update check 1/1 starting...", "error") TestForLogPassFail("Getting temp path...", "error") TestForLogPassFail("sc_installer_version.xml", "error") TestForLogPassFail("XML file: sc_installer_version.xml", "error") TestForLogPassFail("Starting downloader synchronously...", "error") TestForLogPassFail("Downloading", "error") TestForLogPassFail("Selecting regular downloader.", "error") TestForLogPassFail("RockstarDownloader starting.", "error") TestForLogPassFail("RockstarDownloader doing download.", "error") TestForLogPassFail("RockstarDownloader checking URL validity...", "error") TestForLogPassFail("RockstarDownloader checking disk...", "error") TestForLogPassFail("sc_installer_version.xml to determine length...", "error") TestForLogPassFail("Content length", "error") TestForLogPassFail("bytes available; we only need", "error") TestForLogPassFail("RockstarDownloader receiving data...", "error") TestForLogPassFail("GET to patches.rockstargames.com", "error") TestForLogPassFail("Download succeeded.", "error") TestForLogPassFail("Parsing XML...", "error") TestForLogPassFail("Adding RGSC patch (removing any previous).", "error") TestForLogPassFail("Cleaning up XML...", "error") TestForLogPassFail("Checking EXE...", "error") TestForLogPassFail("File version", "error") TestForLogPassFail("Executable version", "error") TestForLogPassFail("Checking 1 patches...", "error") TestForLogPassFail("Patch 1 is already installed.", "error") TestForLogLine("Finished job: JOB_CHECK_FOR_SC_UPDATES") TestForLogPassFail("No patches to install; we are up-to-date.", "Entering state 3 (eSocialClub)") EndSection() StartSection("Launcher SC Init") InsertLogFilePath() TestForLogLine("Entering state 3 (eSocialClub)") TestForLogPassFail("Social Club initializing...", "error") TestForLogPassFail("Starting Social Club thread...", "error") TestForLogPassFail("Loading DLL...", "error") TestForLogPassFail("Calling LoadLibraryExA: C:\\Program Files (x86)\\Rockstar Games\\Social Club\\socialclub64.dll", "error") TestForLogPassFail("Initialising DLL...", "error") TestForLogPassFail("Getting proc address...", "error") TestForLogPassFail("Obtaining interfaces...", "error") TestForLogPassFail("Setting up ROS interface...", "error") TestForLogPassFail("Beginning decryption of ROS Secrets", "error") TestForLogPassFail("Beginning Decryption", "error") TestForLogPassFail("Finished Decryption", "error") TestForLogPassFail("Finished decryption of ROS Secrets", "error") TestForLogPassFail("Initialising RGSC...", "error") TestForLogPassFail("Setting up SCUI...", "error") TestForLogPassFail("Getting activation system...", "error") TestForLogPassFail("Notifying SCUI of OnCreateDevice...", "error") TestForLogPassFail("Attempting to create device...", "error") TestForLogPassFail("Attempting to initialise DX9 COM object...", "error") TestForLogPassFail("Created Direct3D.", "error") TestForLogPassFail("Window size: 800 x 600", "error") TestForLogPassFail("Attempting D3DFMT_UNKNOWN, D3DCREATE_HARDWARE_VERTEXPROCESSING, D3DCREATE_MULTITHREADED...", "error") TestForLogPassFail("Successfully created DX Device!", "error") TestForLogPassFail("Social Club seems to have loaded correctly.", "error") TestForLogPassFail("Entitlement seems to have initialized correctly.", "error") TestForLogPassFail("SCUI is ready to accept commands. Showing SCUI...", "error") TestForLogPassFail("Pre-rendering...", "error") TestForLogPassFail("SCUI is visible.", "error") TestForLogPassFail("Showing DxSurface9.", "error") TestForLogPassFail("SCUI is ready for commands.", "error") TestForLogPassFail("SCUI is visible.", "error") EndSection() StartSection("Launcher Simulate Logging In") InsertLogFilePath() TestForLogLine("SetTextBoxHasFocusV2(true, Nickname/Email") TestForLogLine("SetTextBoxHasFocusV2(false") TestForLogLine("SetTextBoxHasFocusV2(true, Password") TestForLogLine("SetTextBoxHasFocusV2(false") TestForLogLine("Signed-in; continuing.") EndSection() StartSection("Launcher Checking Entitlement") InsertLogFilePath() TestForLogPassFail("Entering state 4 (eCheckForEntitlement)", "error") TestForLogLine("Erasing completed state (eCheckForEntitlement)") EndSection() StartSection("Launcher In-place Download") InsertLogFilePath() TestForLogLine("Entering state 5 (eCheckForInPlaceDownload)") TestForLogPassFail("manifest to determine length", "error") TestForLogPassFail("we only need", "error") TestForLogPassFail("GET to patches.rockstargames.com", "error") TestForLogPassFail("Download succeeded", "error") TestForLogPassFail("x64a.rpf missing", "x64a.rpf seems complete") TestForLogLine("In-place download is not up-to-date; download required") TestForLogLine("Finished job: JOB_CHECK_FOR_IN_PLACE_DOWNLOAD") TestForLogLine("Entering state 12 (eGameInstallAvailable)") TestForLogLine("Posting SCUI message") TestForLogPassFail("AutotestDigitalUpdate pressing update button...", "error") TestForLogPassFail("Inserting new flow state at the front: eDownloadInPlaceData", "error") TestForLogPassFail("Entering state 13 (eDownloadInPlaceData)", "error") TestForLogLine("Beginning job: JOB_DOWNLOAD_IN_PLACE") TestForLogLine("Initialising downloader.") TestForLogLine("Starting downloader synchronously...") TestForLogLine("Selecting chunked downloader.") TestForLogLine("RockstarDownloader starting.") TestForLogLine("RockstarDownloader doing download.") TestForLogLine("RockstarDownloader checking URL validity...") TestForLogLine("RockstarDownloader checking disk...") TestForLogLine("to determine length...") TestForLogLine("Content length") TestForLogLine("available; we only need") TestForLogLine("RockstarDownloader receiving data...") TestForLogLine("RockstarDownloader starting.") TestForLogLine("RockstarDownloader doing download.") TestForLogLine("RockstarDownloader checking URL validity...") TestForLogLine("RockstarDownloader checking disk...") TestForLogLine("hash to determine length...") TestForLogLine("Content length") TestForLogLine("available; we only need") TestForLogLine("RockstarDownloader receiving data...") TestForLogLine("GET to patches.rockstargames.com") TestForLogLine("Download succeeded.") TestForLogLine("[1] Using range header: Range: bytes=") TestForLogLine("[1] Sending request.") TestForLogLine("[1] 16KB @ ") TestForLogLine("Checking hash") TestForLogLine("Hashes match:") TestForLogLine("Hash check succeeded.") TestForLogLine("Starting another chunk.") TestForLogLine("AutotestDigitalUpdate terminating launcher...") EndSection() StartSection("Launcher Clean Shutdown") InsertLogFilePath() TestForLogPassFail("Entering state 30 (eShuttingDown)", "error") TestForLogPassFail("Shutting down.", "error") TestForLogPassFail("Joining WorkerThread.", "error") TestForLogPassFail("Signaling WorkerThread to stop.", "error") TestForLogPassFail("Ending Social Club thread...", "error") TestForLogPassFail("Thread no longer running.", "error") TestForLogPassFail("Shutting down Entitlement...", "error") TestForLogPassFail("Shutting down SCUI...", "error") TestForLogPassFail("Shutting down RGSC...", "error") TestForLogPassFail("Freeing library...", "error") TestForLogPassFail("Uninitializing.", "error") TestForLogPassFail("Social Club destructed.", "error") EndSection() CloseLogFile() end def LauncherPatchTest() random_arg = "-autotest_random_arg_" + rand(100).to_s RsgLog.instance().info("Launching Launcher...") result = system(GAME_DIR + "/GTAVLauncher.exe", random_arg, "-autotest_launcher_selfsignin", "-autotest_launcher_gamepatch") StartSection("Launcher Check For Game Patch") AddResultLine("Execution success", result) AddResultLine("Process return code == 0", $? == 0) OpenLogFile("launcher.log") generated_log = ContinueLogFileSearch(random_arg) CloseLogFile() AddResultLine("Launcher generated a log file", generated_log) if !generated_log EndSection() return end CopyAndOpenLogFile("launcher.log") TestForLogLine("Entering state 6 (eCheckForUpdate)") TestForLogLine("Beginning job: JOB_CHECK_FOR_GAME_UPDATES") TestForLogLine("Dev server has already been checked.") TestForLogLine("Initialising downloader.") TestForLogLine("Checking for updates...") TestForLogLine("There is/are 2 check(s) to do.") TestForLogLine("Update check 1/2 starting...") TestForLogLine("Clearing temporary patch list...") TestForLogLine("Getting temp path...") TestForLogLine("gtav_version.xml") TestForLogLine("Queueing file...") TestForLogLine("Starting file...") TestForLogLine("Starting downloader synchronously...") TestForLogLine("Downloading") TestForLogLine("Selecting regular downloader.") TestForLogLine("RockstarDownloader starting.") TestForLogLine("RockstarDownloader doing download.") TestForLogLine("RockstarDownloader checking URL validity...") TestForLogLine("RockstarDownloader checking disk...") TestForLogLine("gtav_version.xml to determine length...") TestForLogLine("Content length") TestForLogLine("bytes available; we only need") TestForLogLine("RockstarDownloader receiving data...") TestForLogLine("GET to patches.rockstargames.com") TestForLogLine("Download succeeded.") TestForLogLine("Download complete.") TestForLogLine("Synchronous downloading ended.") TestForLogLine("File finished.") TestForLogLine("Parsing XML...") TestForLogLine("Adding game patch.") TestForLogLine("Cleaning up XML...") TestForLogLine("Checking EXE...") TestForLogLine("Unable to get file version info size") TestForLogLine("The system cannot find the file specified.") TestForLogLine("Executable version 0.0.0.0") TestForLogLine("Checking 1 patches...") TestForLogLine("Patch 1 needs installing.") TestForLogLine("Update check 1/2 done.") TestForLogLine("Check 1 was not up-to-date; adding patches.") TestForLogLine("Check 1 patch 1 is not installed; queueing.") TestForLogLine("Update check 2/2 starting...") TestForLogLine("Clearing temporary patch list...") TestForLogLine("Getting temp path...") TestForLogLine("dlc_version.xml") TestForLogLine("Selecting regular downloader.") TestForLogLine("RockstarDownloader starting.") TestForLogLine("RockstarDownloader doing download.") TestForLogLine("RockstarDownloader checking URL validity...") TestForLogLine("RockstarDownloader checking disk...") TestForLogLine("dlc_version.xml to determine length...") TestForLogLine("Content length") TestForLogLine("bytes available; we only need") TestForLogLine("RockstarDownloader receiving data...") TestForLogLine("GET to patches.rockstargames.com") TestForLogLine("Download succeeded.") TestForLogLine("Download complete.") TestForLogLine("Synchronous downloading ended.") TestForLogLine("File finished.") TestForLogLine("Parsing XML...") TestForLogLine("Cleaning up XML...") TestForLogLine("Checking EXE...") TestForLogLine("Executable version 0.0.0.0") TestForLogLine("No patches to check.") TestForLogLine("Update check 2/2 done.") TestForLogLine("Check 2 was up-to-date.") TestForLogLine("No more checks.") TestForLogLine("We have become mandatory.") TestForLogLine("Shutting down downloader.") TestForLogLine("Finished job: JOB_CHECK_FOR_GAME_UPDATES") TestForLogLine("Erasing completed state (eCheckForUpdate)") TestForLogLine("1 patch(es) to install; we are not up-to-date.") TestForLogLine("Flow disrupted due to update available.") EndSection() StartSection("Launcher Download patch") InsertLogFilePath() TestForLogLine("Entering state 9 (eRequiredUpdate)") TestForLogLine("Hiding all controls.") TestForLogLine("Posting SCUI message: ") TestForLogLine("AutotestGamePatch pressing update button...") TestForLogLine("Inserting new flow state at the front: eDownloadUpdate") TestForLogLine("Entering state 15 (eDownloadUpdate)") TestForLogLine("Beginning job: JOB_DOWNLOAD_UPDATE") TestForLogLine("Running patch process.") TestForLogLine("We have (or can acquire) admin rights.") TestForLogLine("1 patch to process.") TestForLogLine("Attempting to download patch:") TestForLogLine("Initialising downloader.") TestForLogLine("Starting downloader synchronously...") TestForLogLine("Downloading http://patches.rockstargames.com/") TestForLogLine("Selecting chunked downloader.") TestForLogLine("RockstarDownloader starting.") TestForLogLine("RockstarDownloader doing download.") TestForLogLine("RockstarDownloader checking URL validity...") TestForLogLine("RockstarDownloader checking disk...") TestForLogLine(" to determine length...") TestForLogLine("Content length") TestForLogLine("bytes available; we only need") TestForLogLine("RockstarDownloader starting.") TestForLogLine("RockstarDownloader doing download.") TestForLogLine("RockstarDownloader checking URL validity...") TestForLogLine("RockstarDownloader checking disk...") TestForLogLine(".hash to determine length...") TestForLogLine("Query failed.") TestForLogLine("[1] Using range header: Range:") TestForLogLine("[1] Sending request.") TestForLogLine("[1] 16KB @") TestForLogLine("No more incomplete ranges.") TestForLogLine("Deleting partfile.") TestForLogLine("Download complete.") TestForLogLine("Synchronous downloading ended.") TestForLogLine("Attempting to install patch.") TestForLogLine("Installing patch") TestForLogLine("About to launch process:") TestForLogLine("Pid:") TestForLogLine("Waiting for handle") TestForLogLine("Finished waiting.") TestForLogLine("Exit code: 0") TestForLogLine("Successfully installed patch ") TestForLogLine("Finished job: JOB_DOWNLOAD_UPDATE") TestForLogLine("Erasing completed state (eDownloadUpdate)") EndSection() StartSection("Launcher Launching Game") InsertLogFilePath() TestForLogLine("Entering state 8 (eLoading)") TestForLogPassFail("Launching game...", "error") TestForLogPassFail("(path: ", "error") TestForLogLine("autotest_launcher_gamepatch killing process") EndSection() end def LauncherErrorScreenTest(title, extra_args, start_error = 1, stop_error = 50) DeleteSomeFiles() errornumber = start_error StartSection(title) AddInfoLine("Adding parameters: " + extra_args.join(",")) while errornumber < stop_error # There's no easy way to say this. It's a hack. # These cases trigger B*2149495, which need SC DLL support. if (errornumber >= 34 && errornumber <= 37) AddInfoLine("[Skipping test "+errornumber.to_s+' due to Bug 2149495]'); errornumber += 1 next end random_arg = "-autotest_random_arg_" + rand(100).to_s RsgLog.instance().info("AutotestFail "+errornumber.to_s+"...") args = [random_arg, "-autotest_launcher_selfsignin", "-autotest_launcher_errors="+errornumber.to_s, "-autotest_launcher_errors_delay="+$SHOW_ERROR_DELAY.to_s, *extra_args] result = system(GAME_DIR + "/GTAVLauncher.exe ", *args) OpenLogFile("launcher.log") generated_log = ContinueLogFileSearch(random_arg) if (!generated_log) AddResultLine("Test "+errornumber.to_s+": Generated log file", false) CloseLogFile() CopyAndOpenLogFile("launcher.log") CloseLogFile() break end errorTest = "Testing error message at " test_line = ContinueLogFileSearchReturnLine(errorTest) if (test_line == nil) break; end indexof = test_line.index(errorTest)+errorTest.length test_location = test_line[indexof,test_line.length-indexof-1] test_filename = test_location.gsub(/(.:.*):.*/, "\\1") test_linenumber = test_location.gsub(/.:.*:(.*)/, "\\1") test_complete_line = ReadLineFromFile(test_filename, test_linenumber.to_i) test_location.gsub!("\\","/") test_file = test_location.gsub(/(.:.*):.*/, "file:///\\1") test_location = errornumber.to_s + ": at #{test_location}" if ($? != 0) AddResultLine(test_location, false) AddCodeLine(test_complete_line) AddInfoLine("Test "+errornumber.to_s+": Appears to have crashed. Exit code: "+$?.to_s) CloseLogFile() CopyAndOpenLogFile("launcher.log") CloseLogFile() break end saw_error_screen = ContinueLogFileSearch("AutotestFail: Notified about reaching error screen") if saw_error_screen AddResultLine(test_location, true) AddCodeLine(test_complete_line) line = ContinueLogFileSearchReturnLine("AutotestFail: Error state:") if (line != nil) line.gsub!(/.*Error state:(.*)/,'\\1') AddSmallInfoLine("Reached error screen:" + line) else AddSmallInfoLine("Reached error screen.") end line = ContinueLogFileSearchReturnLine("AutotestFail: UI state:") if (line != nil) line.gsub!(/.*(UI state:.*)/,'\\1') AddSmallInfoLine(line) end else retrying = ContinueLogFileSearch("AutotestFail: Notified about retrying") if (retrying) AddResultLine(test_location, true) AddCodeLine(test_complete_line) AddInfoLine("Retried.") else if (ContinueLogFileSearch("AutotestFail: Notified that this is not an error")) AddResultLine(test_location, true) AddCodeLine(test_complete_line) AddInfoLine("Not an error.") else AddResultLine(test_location, false) AddCodeLine(test_complete_line) end end end CloseLogFile() if (!saw_error_screen) CopyAndOpenLogFile("launcher.log") CloseLogFile() end errornumber = errornumber + 1 end EndSection() end # Okay! We're done. Now lets actually launch the stuff.... # Handle showing a single error code if parameters.showerror puts puts puts "Which section is the error screen from? (1 or 2)" section = gets.to_i puts "Which error number would you like? (see automated email for numbers)" errornumber = gets.to_i args = ["-autotest_launcher_selfsignin", "-autotest_launcher_errors="+errornumber.to_s, "-noautokill"] if section == 1 args.push("-autotest_launcher_digitalupdate") else args.push("-autotest_launcher_gamepatch") end system(GAME_DIR + "/GTAVLauncher.exe ", *args) exit end StartReport() if !parameters.errorsonly if !parameters.skipupload BootstrapSelfPatchTest() Countdown(10) BootstrapLauncherPatchTest() Countdown(3) end BootstrapLauncherRunTest() Countdown(5) LauncherRunTest() Countdown(3) LauncherPatchTest() Countdown(3) end if !parameters.noerrors LauncherErrorScreenTest("Launcher Error Screens 1", ["-autotest_launcher_digitalupdate"], 1, 60) LauncherErrorScreenTest("Launcher Error Screens 2", ["-autotest_launcher_gamepatch"], 27, 65) #TODO: Test https://dev.ros.rockstargames.com/scui/v2/api/test/resetrecaptcha end EndReport() #log.info("Report: "+$reportHtml) File.open("report.html", "w") do |f| f.write($reportHtml) end if (parameters.emailfrom != nil) && (parameters.emailto != nil) log.info("Mailing out results...") log.info("From: #{parameters.emailfrom}") log.info("To: #{parameters.emailto}") recipients = parameters.emailto.split(";") log.info(recipients.size.to_s + " recipients") message = "From: Launcher Automated Test Tool <#{parameters.emailfrom}>\n" message += "To: " first = true recipients.each { |i| if first first = false else message += ", " end message += "<#{i}>" log.info(" #{i}") } message += "MIME-Version: 1.0\n" message += "Content-type: text/html\n" message += "Subject: Launcher Automated Test Results\n\n" message += $reportHtml Net::SMTP.start('smtp.rockstar.t2.corp') do |smtp| smtp.send_message message, parameters.emailfrom, recipients end end # Exiting log.info("All done! Exiting!")