about summary refs log tree commit homepage
diff options
context:
space:
mode:
authorEric Wong <normalperson@yhbt.net>2010-03-01 08:27:36 +0000
committerEric Wong <normalperson@yhbt.net>2010-03-01 09:22:53 +0000
commit9dc37425107195530b30af80ed9aa1ad72c9be94 (patch)
tree87fb55142a1f79ac4625315761e08b2c0016fe96
parent5682d2879258f3ca06b28d4a1a7671b20f676dac (diff)
downloadzbatery-9dc37425107195530b30af80ed9aa1ad72c9be94.tar.gz
import selected parts of test suite from Rainbows!
-rw-r--r--t/.gitignore4
-rw-r--r--t/GNUmakefile117
-rwxr-xr-xt/bin/content-md5-put36
-rwxr-xr-xt/bin/sha1sum.rb23
-rwxr-xr-xt/bin/unused_listen40
-rwxr-xr-xt/bin/utee12
-rw-r--r--t/large-file-response.ru14
-rw-r--r--t/my-tap-lib.sh200
-rw-r--r--t/sleep.ru13
-rwxr-xr-xt/t0003-reopen-logs.sh107
-rwxr-xr-xt/t0005-large-file-response.sh83
-rw-r--r--t/test-lib.sh155
12 files changed, 804 insertions, 0 deletions
diff --git a/t/.gitignore b/t/.gitignore
new file mode 100644
index 0000000..bee30c6
--- /dev/null
+++ b/t/.gitignore
@@ -0,0 +1,4 @@
+/test-results-*
+/test-bin-*
+/random_blob
+/.dep+*
diff --git a/t/GNUmakefile b/t/GNUmakefile
new file mode 100644
index 0000000..dbd78c4
--- /dev/null
+++ b/t/GNUmakefile
@@ -0,0 +1,117 @@
+# we can run tests in parallel with GNU make
+
+all::
+
+pid := $(shell echo $$PPID)
+
+RUBY = ruby
+zbatery_lib := $(shell cd ../lib && pwd)
+-include ../local.mk
+ifeq ($(RUBY_VERSION),)
+  RUBY_VERSION := $(shell $(RUBY) -e 'puts RUBY_VERSION')
+endif
+
+ifeq ($(RUBY_VERSION),)
+  $(error unable to detect RUBY_VERSION)
+endif
+
+ifeq ($(RUBYLIB),)
+  RUBYLIB := $(zbatery_lib)
+else
+  RUBYLIB := $(zbatery_lib):$(RUBYLIB)
+endif
+export RUBYLIB RUBY_VERSION
+
+models += ThreadPool
+models += ThreadSpawn
+models += Rev
+models += EventMachine
+models += NeverBlock
+models += RevThreadSpawn
+models += RevThreadPool
+
+rp := )
+ONENINE := $(shell case $(RUBY_VERSION) in 1.9.*$(rp) echo true;;esac)
+ifeq ($(ONENINE),true)
+  models += Revactor
+  models += FiberSpawn
+  models += RevFiberSpawn
+  models += FiberPool
+endif
+all_models := $(models) Base
+
+T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
+
+MODEL_T := $(foreach m,$(all_models),$(addprefix $(m).,$(T)))
+$(T): MODELS = $(models)
+
+# some tests can be run with all models
+t0000-simple-http.sh: MODELS = $(all_models)
+t0001-unix-http.sh: MODELS = $(all_models)
+t0002-graceful.sh: MODELS = $(all_models)
+t0002-parser-error.sh: MODELS = $(all_models)
+t0003-reopen-logs.sh: MODELS = $(all_models)
+
+# recursively run per-model tests
+# haven't figured out a good way to make make non-recursive here, yet...
+$(T):
+        $(MAKE) $(foreach m,$(MODELS),$(addprefix $(m).,$@))
+
+$(all_models):
+        $(MAKE) $(filter $@.%,$(MODEL_T))
+
+all:: $(T)
+
+# can't rely on "set -o pipefail" since we don't require bash or ksh93 :<
+t_pfx = trash/$@-$(RUBY_VERSION)
+TEST_OPTS =
+# TRACER = strace -f -o $(t_pfx).strace -s 100000
+# TRACER = /usr/bin/time -o $(t_pfx).time
+
+ifdef V
+  ifeq ($(V),2)
+    TEST_OPTS += --trace
+  else
+    TEST_OPTS += --verbose
+  endif
+endif
+
+test-bin-$(RUBY_VERSION)/zbatery: ruby_bin = $(shell which $(RUBY))
+test-bin-$(RUBY_VERSION)/zbatery: ../bin/zbatery
+        mkdir -p $(@D)
+        install -m 755 $^ $@.$(pid)
+        $(RUBY) -i -p -e '$$_.gsub!(%r{^#!.*$$},"#!$(ruby_bin)")' $@.$(pid)
+        mv $@.$(pid) $@
+
+random_blob:
+        dd if=/dev/urandom bs=1M count=30 of=$@.$(pid)
+        mv $@.$(pid) $@
+
+$(T): random_blob
+
+dependencies := socat curl
+deps := $(addprefix .dep+,$(dependencies))
+$(deps): dep_bin = $(lastword $(subst +, ,$@))
+$(deps):
+        @which $(dep_bin) > $@.$(pid) 2>/dev/null || :
+        @test -s $@.$(pid) || \
+          { echo >&2 "E '$(dep_bin)' not found in PATH=$(PATH)"; exit 1; }
+        @mv $@.$(pid) $@
+dep: $(deps)
+
+$(MODEL_T): export model = $(firstword $(subst ., ,$@))
+$(MODEL_T): script = $(subst $(model).,,$@)
+$(MODEL_T): trash/.gitignore
+$(MODEL_T): export RUBY := $(RUBY)
+$(MODEL_T): export PATH := $(CURDIR)/test-bin-$(RUBY_VERSION):$(PATH)
+$(MODEL_T): test-bin-$(RUBY_VERSION)/zbatery dep
+        $(TRACER) $(SHELL) $(SH_TEST_OPTS) $(script) $(TEST_OPTS)
+
+trash/.gitignore:
+        mkdir -p $(@D)
+        echo '*' > $@
+
+clean:
+        $(RM) -r trash/*.log trash/*.code test-bin-$(RUBY_VERSION)
+
+.PHONY: $(T) clean
diff --git a/t/bin/content-md5-put b/t/bin/content-md5-put
new file mode 100755
index 0000000..01da0bb
--- /dev/null
+++ b/t/bin/content-md5-put
@@ -0,0 +1,36 @@
+#!/usr/bin/env ruby
+# -*- encoding: binary -*-
+# simple chunked HTTP PUT request generator (and just that),
+# it reads stdin and writes to stdout so socat can write to a
+# UNIX or TCP socket (or to another filter or file) along with
+# a Content-MD5 trailer.
+require 'digest/md5'
+$stdout.sync = $stderr.sync = true
+$stdout.binmode
+$stdin.binmode
+
+bs = ENV['bs'] ? ENV['bs'].to_i : 4096
+
+if ARGV.grep("--no-headers").empty?
+  $stdout.write(
+      "PUT / HTTP/1.1\r\n" \
+      "Host: example.com\r\n" \
+      "Transfer-Encoding: chunked\r\n" \
+      "Trailer: Content-MD5\r\n" \
+      "\r\n"
+    )
+end
+
+digest = Digest::MD5.new
+if buf = $stdin.readpartial(bs)
+  begin
+    digest.update(buf)
+    $stdout.write("%x\r\n" % [ buf.size ])
+    $stdout.write(buf)
+    $stdout.write("\r\n")
+  end while $stdin.read(bs, buf)
+end
+
+digest = [ digest.digest ].pack('m').strip
+$stdout.write("0\r\n")
+$stdout.write("Content-MD5: #{digest}\r\n\r\n")
diff --git a/t/bin/sha1sum.rb b/t/bin/sha1sum.rb
new file mode 100755
index 0000000..b602e79
--- /dev/null
+++ b/t/bin/sha1sum.rb
@@ -0,0 +1,23 @@
+#!/usr/bin/env ruby
+# -*- encoding: binary -*-
+
+# Reads from stdin and outputs the SHA1 hex digest of the input this is
+# ONLY used as a last resort, our test code will try to use sha1sum(1),
+# openssl(1), or gsha1sum(1) before falling back to using this.  We try
+# all options first because we have a strong and healthy distrust of our
+# Ruby abilities in general, and *especially* when it comes to
+# understanding (and trusting the implementation of) Ruby 1.9 encoding.
+
+require 'digest/sha1'
+$stdout.sync = $stderr.sync = true
+$stdout.binmode
+$stdin.binmode
+bs = 16384
+digest = Digest::SHA1.new
+if buf = $stdin.read(bs)
+  begin
+    digest.update(buf)
+  end while $stdin.read(bs, buf)
+end
+
+$stdout.syswrite("#{digest.hexdigest}\n")
diff --git a/t/bin/unused_listen b/t/bin/unused_listen
new file mode 100755
index 0000000..b638f54
--- /dev/null
+++ b/t/bin/unused_listen
@@ -0,0 +1,40 @@
+#!/usr/bin/env ruby
+# -*- encoding: binary -*-
+# this is to remain compatible with the unused_port function in the
+# Unicorn test/test_helper.rb file
+require 'socket'
+require 'tmpdir'
+
+default_port = 8080
+addr = ENV['UNICORN_TEST_ADDR'] || '127.0.0.1'
+retries = 100
+base = 5000
+port = sock = lock_path = nil
+
+begin
+  begin
+    port = base + rand(32768 - base)
+    while port == default_port
+      port = base + rand(32768 - base)
+    end
+
+    sock = Socket.new(Socket::AF_INET, Socket::SOCK_STREAM, 0)
+    sock.bind(Socket.pack_sockaddr_in(port, addr))
+    sock.listen(5)
+  rescue Errno::EADDRINUSE, Errno::EACCES
+    sock.close rescue nil
+    retry if (retries -= 1) >= 0
+  end
+
+  # since we'll end up closing the random port we just got, there's a race
+  # condition could allow the random port we just chose to reselect itself
+  # when running tests in parallel with gmake.  Create a lock file while
+  # we have the port here to ensure that does not happen.
+  lock_path = "#{Dir::tmpdir}/unicorn_test.#{addr}:#{port}.lock"
+  lock = File.open(lock_path, File::WRONLY|File::CREAT|File::EXCL, 0600)
+rescue Errno::EEXIST
+  sock.close rescue nil
+  retry
+end
+sock.close rescue nil
+puts %Q(listen=#{addr}:#{port} T_RM_LIST="$T_RM_LIST #{lock_path}")
diff --git a/t/bin/utee b/t/bin/utee
new file mode 100755
index 0000000..7b61fea
--- /dev/null
+++ b/t/bin/utee
@@ -0,0 +1,12 @@
+#!/usr/bin/env ruby
+# -*- encoding: binary -*-
+# tee(1) as distributed on most(all?) systems is buffered in luserspace
+# this only does unbuffered writes (with line-buffered input) to make
+# test output appear in real-time
+$stdin.binmode
+$stdout.binmode
+fp = File.open(ARGV.shift, "wb")
+$stdin.each_line do |line|
+  fp.syswrite line
+  $stdout.syswrite line
+end
diff --git a/t/large-file-response.ru b/t/large-file-response.ru
new file mode 100644
index 0000000..84163c1
--- /dev/null
+++ b/t/large-file-response.ru
@@ -0,0 +1,14 @@
+# lib-large-file-response will stop running if we're not on Linux here
+use Rack::ContentLength
+use Rack::ContentType
+map "/rss" do
+  run lambda { |env|
+    # on Linux, this is in kilobytes
+    GC.start if GC.respond_to?(:start)
+    ::File.read("/proc/self/status") =~ /^VmRSS:\s+(\d+)/
+    [ 200, {}, [ ($1.to_i * 1024).to_s ] ]
+  }
+end
+map "/" do
+  run Rack::File.new(Dir.pwd)
+end
diff --git a/t/my-tap-lib.sh b/t/my-tap-lib.sh
new file mode 100644
index 0000000..ada77ac
--- /dev/null
+++ b/t/my-tap-lib.sh
@@ -0,0 +1,200 @@
+#!/bin/sh
+# Copyright (c) 2009 Eric Wong <normalperson@yhbt.net>
+#
+# TAP-producing shell library for POSIX-compliant Bourne shells We do
+# not _rely_ on Bourne Again features, though we will use "set -o
+# pipefail" from ksh93 or bash 3 if available
+#
+# Only generic, non-project/non-language-specific stuff goes here.  We
+# only have POSIX dependencies for the core tests (without --verbose),
+# though we'll enable useful non-POSIX things if they're available.
+#
+# This test library is intentionally unforgiving, it does not support
+# skipping tests nor continuing after any failure.  Any failures
+# immediately halt execution as do any references to undefined
+# variables.
+#
+# When --verbose is specified, we always prefix stdout/stderr
+# output with "#" to avoid confusing TAP consumers.  Otherwise
+# the normal stdout/stderr streams are redirected to /dev/null
+
+# dup normal stdout(fd=1) and stderr (fd=2) to fd=3 and fd=4 respectively
+# normal TAP output goes to fd=3, nothing should go to fd=4
+exec 3>&1 4>&2
+
+# ensure a sane environment
+TZ=UTC LC_ALL=C LANG=C
+export LANG LC_ALL TZ
+unset CDPATH
+
+# pipefail is non-POSIX, but very useful in ksh93/bash
+( set -o pipefail 2>/dev/null ) && set -o pipefail
+
+SED=${SED-sed}
+
+# Unlike other test frameworks, we are unforgiving and bail immediately
+# on any failures.  We do this because we're lazy about error handling
+# and also because we believe anything broken should not be allowed to
+# propagate throughout the rest of the test
+set -e
+set -u
+
+# name of our test
+T=${0##*/}
+
+t_expect_nr=-1
+t_nr=0
+t_current=
+t_complete=false
+
+# list of files to remove unconditionally on exit
+T_RM_LIST=
+
+# list of files to remove only on successful exit
+T_OK_RM_LIST=
+
+# emit output to stdout, it'll be parsed by the TAP consumer
+# so it must be TAP-compliant output
+t_echo () {
+        echo >&3 "$@"
+}
+
+# emits non-parsed information to stdout, it will be prefixed with a '#'
+# to not throw off TAP consumers
+t_info () {
+        t_echo '#' "$@"
+}
+
+# exit with an error and print a diagnostic
+die () {
+        echo >&2 "$@"
+        exit 1
+}
+
+# our at_exit handler, it'll fire for all exits except SIGKILL (unavoidable)
+t_at_exit () {
+        code=$?
+        set +e
+        if test $code -eq 0
+        then
+                $t_complete || {
+                        t_info "t_done not called"
+                        code=1
+                }
+        elif test -n "$t_current"
+        then
+                t_echo "not ok $t_nr - $t_current"
+        fi
+        if test $t_expect_nr -ne -1
+        then
+                test $t_expect_nr -eq $t_nr || {
+                        t_info "planned $t_expect_nr tests but ran $t_nr"
+                        test $code -ne 0 || code=1
+                }
+        fi
+        $t_complete || {
+                t_info "unexpected test failure"
+                test $code -ne 0 || code=1
+        }
+        rm -f $T_RM_LIST
+        test $code -eq 0 && rm -f $T_OK_RM_LIST
+        set +x
+        exec >&3 2>&4
+        t_close_fds
+        exit $code
+}
+
+# close test-specific extra file descriptors
+t_close_fds () {
+        exec 3>&- 4>&-
+}
+
+# call this at the start of your test to specify the number of tests
+# you plan to run
+t_plan () {
+        test "$1" -ge 1 || die "must plan at least one test"
+        test $t_expect_nr -eq -1 || die "tried to plan twice in one test"
+        t_expect_nr=$1
+        shift
+        t_echo 1..$t_expect_nr "#" "$@"
+        trap t_at_exit EXIT
+}
+
+_t_checkup () {
+        test $t_expect_nr -le 0 && die "no tests planned"
+        test -n "$t_current" && t_echo "ok $t_nr - $t_current"
+        true
+}
+
+# finalizes any previously test and starts a new one
+t_begin () {
+        _t_checkup
+        t_nr=$(( $t_nr + 1 ))
+        t_current="$1"
+
+        # just in case somebody wanted to cheat us:
+        set -e
+}
+
+# finalizes the current test without starting a new one
+t_end () {
+        _t_checkup
+        t_current=
+}
+
+# run this to signify the end of your test
+t_done () {
+        _t_checkup
+        t_current=
+        t_complete=true
+        test $t_expect_nr -eq $t_nr || exit 1
+        exit 0
+}
+
+# create and assign named-pipes to variable _names_ passed to this function
+t_fifos () {
+        for _id in "$@"
+        do
+                _name=$_id
+                _tmp=$(mktemp -t $T.$$.$_id.XXXXXXXX)
+                eval "$_id=$_tmp"
+                rm -f $_tmp
+                mkfifo $_tmp
+                T_RM_LIST="$T_RM_LIST $_tmp"
+        done
+}
+
+t_verbose=false t_trace=false
+
+while test "$#" -ne 0
+do
+        arg="$1"
+        shift
+        case $arg in
+        -v|--verbose) t_verbose=true ;;
+        --trace) t_trace=true t_verbose=true ;;
+        *) die "Unknown option: $arg" ;;
+        esac
+done
+
+# we always only setup stdout, nothing should end up in the "real" stderr
+if $t_verbose
+then
+        if test x"$(which mktemp 2>/dev/null)" = x
+        then
+                die "mktemp(1) not available for --verbose"
+        fi
+        t_fifos t_stdout t_stderr
+
+        (
+                # use a subshell so seds are not waitable
+                $SED -e 's/^/#: /' $t_stdout &
+                $SED -e 's/^/#! /' $t_stderr &
+        ) &
+        exec > $t_stdout 2> $t_stderr
+else
+        exec > /dev/null 2> /dev/null
+fi
+
+$t_trace && set -x
+true
diff --git a/t/sleep.ru b/t/sleep.ru
new file mode 100644
index 0000000..b57efc3
--- /dev/null
+++ b/t/sleep.ru
@@ -0,0 +1,13 @@
+use Rack::ContentLength
+
+run lambda { |env|
+  /\A100-continue\z/i =~ env['HTTP_EXPECT'] and return [ 100, {}, [] ]
+
+  env['rack.input'].read
+  nr = 1
+  env["PATH_INFO"] =~ %r{/([\d\.]+)\z} and nr = $1.to_f
+
+  Rainbows.sleep(nr)
+
+  [ 200, {'Content-Type' => 'text/plain'}, [ "Hello\n" ] ]
+}
diff --git a/t/t0003-reopen-logs.sh b/t/t0003-reopen-logs.sh
new file mode 100755
index 0000000..91c3dc4
--- /dev/null
+++ b/t/t0003-reopen-logs.sh
@@ -0,0 +1,107 @@
+#!/bin/sh
+# don't set nr_client for Rev, only _one_ app running at once :x
+nr_client=${nr_client-2}
+. ./test-lib.sh
+
+t_plan 19 "reopen rotated logs"
+
+t_begin "setup and startup" && {
+        rtmpfiles curl_out curl_err r_rot
+        zbatery_setup $model
+        zbatery -D sleep.ru -c $unicorn_config
+        zbatery_wait_start
+}
+
+t_begin "ensure server is responsive" && {
+        curl -sSf http://$listen/ >/dev/null
+}
+
+t_begin "start $nr_client concurrent requests" && {
+        start=$(date +%s)
+        for i in $(awk "BEGIN{for(i=0;i<$nr_client;++i) print i}" </dev/null)
+        do
+                ( curl -sSf http://$listen/2 >> $curl_out 2>> $curl_err ) &
+        done
+}
+
+t_begin "ensure stderr log is clean" && check_stderr
+
+t_begin "external log rotation" && {
+        rm -f $r_rot
+        mv $r_err $r_rot
+}
+
+t_begin "send reopen log signal (USR1)" && {
+        kill -USR1 $zbatery_pid
+}
+
+t_begin "wait for rotated log to reappear" && {
+        nr=60
+        while ! test -f $r_err && test $nr -ge 0
+        do
+                sleep 1
+                nr=$(( $nr - 1 ))
+        done
+}
+
+t_begin "wait for worker to reopen logs" && {
+        nr=60
+        re="worker=.* done reopening logs"
+        while ! grep "$re" < $r_err >/dev/null && test $nr -ge 0
+        do
+                sleep 1
+                nr=$(( $nr - 1 ))
+        done
+}
+
+dbgcat r_rot
+dbgcat r_err
+
+t_begin "wait curl requests to finish" && {
+        wait
+        t_info elapsed=$(( $(date +%s) - $start ))
+}
+
+t_begin "ensure no errors from curl" && {
+        test ! -s $curl_err
+}
+
+t_begin "curl got $nr_client responses" && {
+        test "$(wc -l < $curl_out)" -eq $nr_client
+}
+
+t_begin "all responses were identical" && {
+        nr=$(sort < $curl_out | uniq | wc -l)
+        test "$nr" -eq 1
+}
+
+t_begin 'response was "Hello"' && {
+        test x$(sort < $curl_out | uniq) = xHello
+}
+
+t_begin "current server stderr is clean" && check_stderr
+
+t_begin "rotated stderr is clean" && {
+        check_stderr $r_rot
+}
+
+t_begin "server is now writing logs to new stderr" && {
+        before_rot=$(wc -c < $r_rot)
+        before_err=$(wc -c < $r_err)
+        curl -sSfv http://$listen/
+        after_rot=$(wc -c < $r_rot)
+        after_err=$(wc -c < $r_err)
+        test $after_rot -eq $before_rot
+        test $after_err -gt $before_err
+}
+
+t_begin "stop server" && {
+        kill $zbatery_pid
+}
+
+dbgcat r_err
+
+t_begin "current server stderr is clean" && check_stderr
+t_begin "rotated stderr is clean" && check_stderr $r_rot
+
+t_done
diff --git a/t/t0005-large-file-response.sh b/t/t0005-large-file-response.sh
new file mode 100755
index 0000000..2deba61
--- /dev/null
+++ b/t/t0005-large-file-response.sh
@@ -0,0 +1,83 @@
+#!/bin/sh
+. ./test-lib.sh
+test -r random_blob || die "random_blob required, run with 'make $0'"
+
+if ! grep -v ^VmRSS: /proc/self/status >/dev/null 2>&1
+then
+        t_info "skipping, can't read RSS from /proc/self/status"
+        exit 0
+fi
+
+t_plan 10 "large file response slurp avoidance for $model"
+
+t_begin "setup and startup" && {
+        rtmpfiles curl_out
+        zbatery_setup $model
+        # can't load Rack::Lint here since it'll cause Rev to slurp
+        zbatery -E none -D large-file-response.ru -c $unicorn_config
+        zbatery_wait_start
+}
+
+t_begin "read random blob size" && {
+        random_blob_size=$(wc -c < random_blob)
+}
+
+t_begin "read current RSS" && {
+        curl -v http://$listen/rss
+        dbgcat r_err
+        rss_before=$(curl -sSfv http://$listen/rss)
+        t_info "rss_before=$rss_before"
+}
+
+t_begin "send a series HTTP/1.1 requests sequentially" && {
+        for i in a b c
+        do
+                size=$( (curl -sSfv http://$listen/random_blob &&
+                         echo ok >$ok) |wc -c)
+                test $size -eq $random_blob_size
+                test xok = x$(cat $ok)
+        done
+}
+
+# this was a problem during development
+t_begin "HTTP/1.0 test" && {
+        size=$( (curl -0 -sSfv http://$listen/random_blob &&
+                 echo ok >$ok) |wc -c)
+        test $size -eq $random_blob_size
+        test xok = x$(cat $ok)
+}
+
+t_begin "HTTP/0.9 test" && {
+        (
+                printf 'GET /random_blob\r\n'
+                cat $fifo > $tmp &
+                wait
+                echo ok > $ok
+        ) | socat - TCP:$listen > $fifo
+        cmp $tmp random_blob
+        test xok = x$(cat $ok)
+}
+
+dbgcat r_err
+
+t_begin "read RSS again" && {
+        curl -v http://$listen/rss
+        rss_after=$(curl -sSfv http://$listen/rss)
+        t_info "rss_after=$rss_after"
+}
+
+t_begin "shutdown server" && {
+        kill -QUIT $zbatery_pid
+}
+
+t_begin "compare RSS before and after" && {
+        diff=$(( $rss_after - $rss_before ))
+        t_info "test diff=$diff < orig=$random_blob_size"
+        test $diff -le $random_blob_size
+}
+
+dbgcat r_err
+
+t_begin "check stderr" && check_stderr
+
+t_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
new file mode 100644
index 0000000..7ea07e7
--- /dev/null
+++ b/t/test-lib.sh
@@ -0,0 +1,155 @@
+#!/bin/sh
+# Copyright (c) 2009 Rainbows! developers
+. ./my-tap-lib.sh
+
+set +u
+if test -z "$model"
+then
+        # defaulting to Base would unfortunately fail some concurrency tests
+        model=ThreadSpawn
+        t_info "model undefined, defaulting to $model"
+fi
+
+set -e
+RUBY="${RUBY-ruby}"
+RUBY_VERSION=${RUBY_VERSION-$($RUBY -e 'puts RUBY_VERSION')}
+t_pfx=$PWD/trash/$model.$T-$RUBY_VERSION
+set -u
+
+PATH=$PWD/bin:$PATH
+export PATH
+
+test -x $PWD/bin/unused_listen || die "must be run in 't' directory"
+
+wait_for_pid () {
+        path="$1"
+        nr=30
+        while ! test -s "$path" && test $nr -gt 0
+        do
+                nr=$(($nr - 1))
+                sleep 1
+        done
+}
+
+# requires $1 and prints out the value of $2
+require_check () {
+        lib=$1
+        const=$2
+        if ! $RUBY -r$lib -e "puts $const" >/dev/null 2>&1
+        then
+                t_info "skipping $T since we don't have $lib"
+                exit 0
+        fi
+}
+
+# given a list of variable names, create temporary files and assign
+# the pathnames to those variables
+rtmpfiles () {
+        for id in "$@"
+        do
+                name=$id
+                _tmp=$t_pfx.$id
+                eval "$id=$_tmp"
+
+                case $name in
+                *fifo)
+                        rm -f $_tmp
+                        mkfifo $_tmp
+                        T_RM_LIST="$T_RM_LIST $_tmp"
+                        ;;
+                *socket)
+                        rm -f $_tmp
+                        T_RM_LIST="$T_RM_LIST $_tmp"
+                        ;;
+                *)
+                        > $_tmp
+                        T_OK_RM_LIST="$T_OK_RM_LIST $_tmp"
+                        ;;
+                esac
+        done
+}
+
+dbgcat () {
+        id=$1
+        eval '_file=$'$id
+        echo "==> $id <=="
+        sed -e "s/^/$id:/" < $_file
+}
+
+check_stderr () {
+        set +u
+        _r_err=${1-${r_err}}
+        set -u
+        if grep -i Error $_r_err
+        then
+                die "Errors found in $_r_err"
+        elif grep SIGKILL $_r_err
+        then
+                die "SIGKILL found in $_r_err"
+        fi
+}
+
+# zbatery_setup [ MODEL [ WORKER_CONNECTIONS ] ]
+zbatery_setup () {
+        eval $(unused_listen)
+        rtmpfiles unicorn_config pid r_err r_out fifo tmp ok
+        cat > $unicorn_config <<EOF
+listen "$listen"
+pid "$pid"
+stderr_path "$r_err"
+stdout_path "$r_out"
+
+after_fork do |server, worker|
+  # test script will block while reading from $fifo,
+  # so notify the script on the first worker we spawn
+  # by opening the FIFO
+  if worker.nr == 0
+    File.open("$fifo", "wb") { |fp| fp.syswrite "START" }
+  end
+end
+EOF
+        {
+                # set a higher default for tests since we run heavily-loaded
+                # boxes and sometimes sleep 1s in tests
+                kato=5
+                echo 'Rainbows! do'
+                if test $# -ge 1
+                then
+                        echo "  use :$1"
+                        test $# -eq 2 && echo "  worker_connections $2"
+                        if test $# -eq 3
+                        then
+                                echo "  keepalive_timeout $3"
+                        else
+                                echo "  keepalive_timeout $kato"
+                        fi
+                else
+                        echo "  use :$model"
+                        echo "  keepalive_timeout $kato"
+                fi
+                echo end
+        } >> $unicorn_config
+}
+
+zbatery_wait_start () {
+        # "cat $fifo" will block until the before_fork hook is called in
+        # the Unicorn config file
+        test xSTART = x"$(cat $fifo)"
+        zbatery_pid=$(cat $pid)
+}
+
+rsha1 () {
+        _cmd="$(which sha1sum 2>/dev/null || :)"
+        test -n "$_cmd" || _cmd="$(which openssl 2>/dev/null || :) sha1"
+        test "$_cmd" != " sha1" || _cmd="$(which gsha1sum 2>/dev/null || :)"
+
+        # last resort, see comments in sha1sum.rb for reasoning
+        test -n "$_cmd" || _cmd=sha1sum.rb
+        expr "$($_cmd < random_blob)" : '\([a-f0-9]\{40\}\)'
+}
+
+case $model in
+Rev) require_check rev Rev::VERSION ;;
+Revactor) require_check revactor Revactor::VERSION ;;
+EventMachine) require_check eventmachine EventMachine::VERSION ;;
+esac