From a67478488f1949879a9b8718dde8bff920c3d307 Mon Sep 17 00:00:00 2001
From: Mike McQuaid
Date: Tue, 17 Apr 2012 13:17:24 -0500
Subject: Add Brew Test Bot for comprehensive testing.
Closes Homebrew/homebrew#11642.
---
.../cmds/brew-test-bot.commit.html.erb | 24 ++
Library/Contributions/cmds/brew-test-bot.css | 75 ++++++
.../cmds/brew-test-bot.index.html.erb | 23 ++
Library/Contributions/cmds/brew-test-bot.rb | 277 +++++++++++++++++++++
4 files changed, 399 insertions(+)
create mode 100644 Library/Contributions/cmds/brew-test-bot.commit.html.erb
create mode 100644 Library/Contributions/cmds/brew-test-bot.css
create mode 100644 Library/Contributions/cmds/brew-test-bot.index.html.erb
create mode 100755 Library/Contributions/cmds/brew-test-bot.rb
(limited to 'Library')
diff --git a/Library/Contributions/cmds/brew-test-bot.commit.html.erb b/Library/Contributions/cmds/brew-test-bot.commit.html.erb
new file mode 100644
index 000000000..c820263a7
--- /dev/null
+++ b/Library/Contributions/cmds/brew-test-bot.commit.html.erb
@@ -0,0 +1,24 @@
+
+
+
+ BrewBot: <%= @test.name %>
+ BrewBot
+
+
+
+
+
+
<%= @test.name %> <%= DateTime.now.strftime "%T %D" %>
+
+
+
+
diff --git a/Library/Contributions/cmds/brew-test-bot.css b/Library/Contributions/cmds/brew-test-bot.css
new file mode 100644
index 000000000..3fcefa59a
--- /dev/null
+++ b/Library/Contributions/cmds/brew-test-bot.css
@@ -0,0 +1,75 @@
+body {
+ background: #27221a;
+ color: #f6e6cc;
+ font-family: Helvetica, Arial, sans-serif;
+ font-size: 1.5em;
+}
+
+h1, h2 {
+ text-align: center;
+ font-weight: bold;
+}
+
+h1 {
+ font-size: 3em;
+ padding-top: 0.5em;
+ padding-bottom: 0;
+ margin-bottom: 0;
+ text-shadow: 1px 1px 10px rgba(0,0,0,0.25);
+ color: #D7AF72;
+ letter-spacing: -3px;
+}
+
+h2 {
+ font-size: 0.8em;
+ margin-top: 0;
+ padding-top: 0;
+ padding-bottom: 1em;
+ color: #A1804C;
+}
+
+h1 a:link, h1 a:visited, h2 a:link, h2 a:visited {
+ text-decoration: none;
+}
+
+table {
+ background-color: rgba(0, 0, 0, 0.30);
+ color: white;
+ border-radius: 0.4em;
+ padding-top: 1em;
+ padding-bottom: 1em;
+ font-size: inherit;
+ font-family: monospace;
+ list-style-type: none;
+ margin-left: auto;
+ margin-right: auto;
+}
+
+td {
+ padding-left: 1em;
+ padding-right: 1em;
+}
+
+.prompt {
+ color: #E3D796;
+}
+
+.status {
+ text-align: right;
+}
+
+.running a:link, .running a:visited {
+ color: orange;
+}
+
+.passed a:link, .passed a:visited {
+ color: green;
+}
+
+.failed a:link, .failed a:visited {
+ color: red;
+}
+
+a:active, a:visited {
+ color: inherit;
+}
diff --git a/Library/Contributions/cmds/brew-test-bot.index.html.erb b/Library/Contributions/cmds/brew-test-bot.index.html.erb
new file mode 100644
index 000000000..d5c5d6913
--- /dev/null
+++ b/Library/Contributions/cmds/brew-test-bot.index.html.erb
@@ -0,0 +1,23 @@
+
+
+
+ BrewBot
+
+
+
+
+
+
diff --git a/Library/Contributions/cmds/brew-test-bot.rb b/Library/Contributions/cmds/brew-test-bot.rb
new file mode 100755
index 000000000..3699bd7d9
--- /dev/null
+++ b/Library/Contributions/cmds/brew-test-bot.rb
@@ -0,0 +1,277 @@
+# Comprehensively test a formula or pull request.
+#
+# Usage: brew test-bot [options...]
+#
+# Options:
+# --log: Writes log files under ./brewbot/
+# --html: Writes html and log files under ./brewbot/
+# --comment: Comment on the pull request
+# --clean: Clean the Homebrew directory. Very dangerous. Use with care.
+
+require 'utils'
+require 'date'
+
+HOMEBREW_CONTRIBUTED_CMDS = HOMEBREW_REPOSITORY + "Library/Contributions/cmds/"
+
+class Step
+ attr_reader :command
+ attr_accessor :status
+
+ def initialize test, command
+ @test = test
+ @category = test.category
+ @command = command
+ @name = command.split[1].delete '-'
+ @status = :running
+ @test.steps << self
+ write_html
+ end
+
+ def log_file_path full_path=true
+ return "/dev/null" unless ARGV.include? "--log" or ARGV.include? "--html"
+ file = "#{@category}.#{@name}.txt"
+ return file unless @test.log_root and full_path
+ @test.log_root + file
+ end
+
+ def status_colour
+ case @status
+ when :passed then "green"
+ when :running then "orange"
+ when :failed then "red"
+ end
+ end
+
+ def status_upcase
+ @status.to_s.upcase
+ end
+
+ def puts_command
+ print "#{Tty.blue}==>#{Tty.white} #{@command}#{Tty.reset}"
+ tabs = (80 - "PASSED".length + 1 - @command.length) / 8
+ tabs.times{ print "\t" }
+ $stdout.flush
+ end
+
+ def puts_result
+ puts "#{Tty.send status_colour}#{status_upcase}#{Tty.reset}"
+ end
+
+ def write_html
+ return unless @test.log_root and ARGV.include? "--html"
+
+ open(@test.log_root + "index.html", "w") do |index|
+ commit_html, css = @test.commit_html_and_css
+ index.write commit_html.result binding
+ end
+ end
+
+ def self.run test, command
+ step = new test, command
+ step.puts_command
+ `#{step.command} &>#{step.log_file_path}`
+ step.status = $?.success? ? :passed : :failed
+ step.puts_result
+ step.write_html
+ end
+end
+
+class Test
+ attr_reader :log_root, :category, :name
+ attr_reader :core_changed, :formulae
+ attr_accessor :steps
+
+ @@css = @@index_html = @@commit_html = nil
+
+ def commit_html_and_css
+ return @@commit_html, @@css
+ end
+
+ def initialize arg
+ begin
+ Formula.factory arg
+ rescue FormulaUnavailableError
+ ofail "#{arg} is not a pull request number or formula." unless arg.to_i > 0
+ @url = arg
+ @formulae = []
+ else
+ @url = nil
+ @formulae = [arg]
+ end
+
+ @start_sha1 = nil
+ @category = __method__
+ @steps = []
+ @core_changed = false
+ @brewbot_root = Pathname.pwd + "brewbot"
+ FileUtils.mkdir_p @brewbot_root if ARGV.include? "--log" or ARGV.include? "--html"
+
+ if ARGV.include? "--html" and not @@css
+ require 'erb'
+ root = HOMEBREW_CONTRIBUTED_CMDS
+ @@css = IO.read root + "brew-test-bot.css"
+ @@index_html = ERB.new IO.read root + "brew-test-bot.index.html.erb"
+ @@commit_html = ERB.new IO.read root + "brew-test-bot.commit.html.erb"
+ end
+ end
+
+ def write_root_html status
+ return unless ARGV.include? "--html"
+
+ FileUtils.mv Dir.glob("*.txt"), @log_root
+ open(@log_root + "status.txt", "w") do |file|
+ file.write status
+ end
+
+ dirs = []
+ dates = []
+ statuses = []
+
+ Pathname.glob("#{@brewbot_root}/*/status.txt").each do |result|
+ dirs << result.dirname.basename
+ status_file = result.dirname + "status.txt"
+ dates << File.mtime(status_file).strftime("%T %D")
+ statuses << IO.read(status_file)
+ end
+
+ open(@brewbot_root + "index.html", "w") do |index|
+ css = @@css
+ index.write @@index_html.result binding
+ end
+ end
+
+ def download
+ def current_sha1
+ `git rev-parse --short HEAD`.strip
+ end
+
+ def current_branch
+ `git symbolic-ref HEAD`.slice!("refs/heads/").strip
+ end
+
+ @category = __method__
+ if @url
+ `git am --abort 2>/dev/null`
+ test "brew update" if current_branch == "master"
+ @start_sha1 = current_sha1
+ test "brew pull --clean #{@url}"
+ end_sha1 = current_sha1
+ else
+ @start_sha1 = end_sha1 = current_sha1
+ end
+
+ name_prefix = @url ? @url : @formulae.first
+ @name = "#{name_prefix}-#{end_sha1}"
+ @log_root = @brewbot_root + @name
+ FileUtils.mkdir_p @log_root if ARGV.include? "--log" or ARGV.include? "--html"
+
+ write_root_html :running
+
+ return unless @url and @start_sha1 != end_sha1 and steps.last.status == :passed
+
+ `git diff #{@start_sha1}..#{end_sha1} --name-status`.each_line do |line|
+ status, filename = line.split
+ # Don't try and do anything to removed files.
+ if (status == 'A' or status == 'M')
+ if filename.include? '/Formula/'
+ @formulae << File.basename(filename, '.rb')
+ end
+ end
+ if filename.include? '/Homebrew/' or filename.include? 'bin/brew'
+ @homebrew_changed = true
+ end
+ end
+ end
+
+ def setup
+ @category = __method__
+
+ test "brew doctor"
+ test "brew --env"
+ test "brew --config"
+ end
+
+ def formula formula
+ @category = __method__.to_s + ".#{formula}"
+
+ test "brew audit #{formula}"
+ test "brew install --verbose --build-bottle #{formula}"
+ return unless steps.last.status == :passed
+ test "brew test #{formula}"
+ test "brew bottle #{formula}"
+ test "brew uninstall #{formula}"
+ end
+
+ def homebrew
+ @category = __method__
+ test "brew tests"
+ end
+
+ def cleanup
+ @category = __method__
+ if ARGV.include? "--clean"
+ test "git reset --hard origin/master"
+ test "git clean --force -dx"
+ else
+ `git diff --exit-code HEAD 2>/dev/null`
+ ofail "Uncommitted changes, aborting." unless $?.success?
+ test "git reset --hard #{@start_sha1}" if @start_sha1
+ end
+ end
+
+ def test cmd
+ Step.run self, cmd
+ end
+
+ def check_results
+ message = "All tests passed and raring to brew."
+
+ status = :passed
+ steps.each do |step|
+ case step.status
+ when :passed then next
+ when :running then raise
+ when :failed then
+ if status == :passed
+ status = :failed
+ message = ""
+ end
+ message += "#{step.command}: #{step.status.to_s.upcase}\n"
+ end
+ end
+
+ write_root_html status
+
+ if ARGV.include? "--comment" and @url
+ username, password = IO.read(File.expand_path('~/.brewbot')).split(':')
+ url = "https://api.github.com/repos/mxcl/homebrew/issues/#{@url}/comments"
+ require 'vendor/multi_json'
+ json = MultiJson.encode(:body => message)
+ curl url, "-X", "POST", "--user", "#{username}:#{password}", "--data", json, "-o", "/dev/null"
+ end
+ end
+
+ def self.run url
+ test = new url
+ test.cleanup
+ test.download
+ test.setup
+ test.formulae.each do |f|
+ test.formula f
+ end
+ test.homebrew if test.core_changed
+ test.cleanup
+
+ test.check_results
+ end
+end
+
+if ARGV.empty?
+ ofail 'This command requires at least one argument containing a pull request number or formula.'
+end
+
+Dir.chdir HOMEBREW_REPOSITORY
+
+ARGV.named.each do|arg|
+ Test.run arg
+end
--
cgit v1.2.3