From 903766ba0d278cb55d08e072c4c96c1d7f0dee8d Mon Sep 17 00:00:00 2001 From: Eric Wong Date: Sun, 4 Oct 2009 16:38:08 -0700 Subject: Add new test suite and basic cases I'd rather write shell scripts in shell than shell scripts in Ruby like was done with Unicorn. We're a *nix-only project so we'll embrace *nix tools to their fullest extent and as a pleasant side-effect these test cases are immune to internal API changes. --- GNUmakefile | 1 - t/.gitignore | 4 +++ t/GNUmakefile | 64 ++++++++++++++++++++++++++++++++++++++++++++ t/bin/unused_listen | 39 +++++++++++++++++++++++++++ t/t0000-basic.sh | 18 +++++++++++++ t/t1000-thread-pool-basic.sh | 53 ++++++++++++++++++++++++++++++++++++ t/t3000-revactor-basic.sh | 52 +++++++++++++++++++++++++++++++++++ t/test-lib.sh | 41 ++++++++++++++++++++++++++++ 8 files changed, 271 insertions(+), 1 deletion(-) create mode 100644 t/.gitignore create mode 100644 t/GNUmakefile create mode 100755 t/bin/unused_listen create mode 100755 t/t0000-basic.sh create mode 100755 t/t1000-thread-pool-basic.sh create mode 100755 t/t3000-revactor-basic.sh create mode 100644 t/test-lib.sh diff --git a/GNUmakefile b/GNUmakefile index 63ba4b1..a8274e7 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -8,7 +8,6 @@ GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE @./GIT-VERSION-GEN -include GIT-VERSION-FILE -include local.mk -ruby_bin := $(shell which $(ruby)) ifeq ($(DLEXT),) # "so" for Linux DLEXT := $(shell $(ruby) -rrbconfig -e 'puts Config::CONFIG["DLEXT"]') endif diff --git a/t/.gitignore b/t/.gitignore new file mode 100644 index 0000000..7cb5e32 --- /dev/null +++ b/t/.gitignore @@ -0,0 +1,4 @@ +/test-results-* +/test-bin-* +/*.code +/*.log diff --git a/t/GNUmakefile b/t/GNUmakefile new file mode 100644 index 0000000..1a95992 --- /dev/null +++ b/t/GNUmakefile @@ -0,0 +1,64 @@ +# we can run tests in parallel with GNU make + +all:: + +ruby = ruby +rainbows_lib := $(shell cd ../lib && pwd) +-include ../local.mk +ifeq ($(RUBY_VERSION),) + RUBY_VERSION := $(shell $(ruby) -e 'puts RUBY_VERSION') +endif + +ifeq ($(RUBYLIB),) + RUBYLIB := $(rainbows_lib) +else + RUBYLIB := $(rainbows_lib):$(RUBYLIB) +endif +export RUBYLIB + +T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh) + +all:: $(T) + +# can't rely on "set -o pipefail" since we don't require bash or ksh93 :< +t_code = $@-$(RUBY_VERSION).code +t_log = $@-$(RUBY_VERSION).log +t_run = $(TRACER) $(SHELL) $(TEST_OPTS) $@ + +# prefix stdout messages with ':', and stderr messages with '!' +t_wrap = ( ( ( $(RM) $(t_code); \ + $(t_run); \ + echo $$? > $(t_code) ) \ + | sed 's/^/$(pfx):/' 1>&3 ) 2>&1 \ + | sed 's/^/$(pfx)!/' 1>&2 ) 3>&1 + +ifndef V + quiet_pre = @echo '* $@'; + quiet_post = > $(t_log) 2>&1; exit $$(cat $(t_code)) + pfx = +else + quiet_pre = @echo '* $@'; + quiet_post = 2>&1 | tee $(t_log); exit $$(cat $(t_code)) + pfx = $@ +endif + +# TRACER='strace -f -o $@.strace -s 100000' +run_test = $(quiet_pre) ( $(t_wrap) ) $(quiet_post) + +test-bin-$(RUBY_VERSION)/rainbows: ruby_bin = $(shell which $(ruby)) +test-bin-$(RUBY_VERSION)/rainbows: ../bin/rainbows + mkdir -p $(@D) + install -m 755 $^ $@+ + $(ruby) -i -p -e '$$_.gsub!(%r{^#!.*$$},"#!$(ruby_bin)")' $@+ + cmp $@+ $@ || mv $@+ $@ + $(RM) $@+ + +$(T): export ruby := $(ruby) +$(T): export PATH := $(CURDIR)/test-bin-$(RUBY_VERSION):$(PATH) +$(T): test-bin-$(RUBY_VERSION)/rainbows + $(run_test) + +clean: + $(RM) -r *.log *.code test-bin-$(RUBY_VERSION) + +.PHONY: $(T) clean diff --git a/t/bin/unused_listen b/t/bin/unused_listen new file mode 100755 index 0000000..c13f97d --- /dev/null +++ b/t/bin/unused_listen @@ -0,0 +1,39 @@ +#!/usr/bin/env ruby +# 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 "listen=#{addr}:#{port} lock_path=#{lock_path}" diff --git a/t/t0000-basic.sh b/t/t0000-basic.sh new file mode 100755 index 0000000..7c5c754 --- /dev/null +++ b/t/t0000-basic.sh @@ -0,0 +1,18 @@ +#!/bin/sh +. ./test-lib.sh + +eval $(unused_listen) +config_ru=$(mktemp -t rainbows.$$.XXXXXXXX.config.ru) +pid=$(mktemp -t rainbows.$$.XXXXXXXX.pid) +TEST_RM_LIST="$TEST_RM_LIST $config_ru $lock_path" + +cat > $config_ru <<\EOF +use Rack::ContentLength +use Rack::ContentType +run lambda { |env| [ 200, {}, [ env.inspect << "\n" ] ] } +EOF + +rainbows $config_ru -l $listen --pid $pid & +wait_for_pid $pid +curl -sSfv http://$listen/ +kill $(cat $pid) diff --git a/t/t1000-thread-pool-basic.sh b/t/t1000-thread-pool-basic.sh new file mode 100755 index 0000000..109f5ae --- /dev/null +++ b/t/t1000-thread-pool-basic.sh @@ -0,0 +1,53 @@ +#!/bin/sh +. ./test-lib.sh + +eval $(unused_listen) +config_ru=$(mktemp -t rainbows.$$.XXXXXXXX.config.ru) +unicorn_config=$(mktemp -t rainbows.$$.XXXXXXXX.unicorn.rb) +curl_out=$(mktemp -t rainbows.$$.XXXXXXXX.curl.out) +curl_err=$(mktemp -t rainbows.$$.XXXXXXXX.curl.err) +pid=$(mktemp -t rainbows.$$.XXXXXXXX.pid) +TEST_RM_LIST="$TEST_RM_LIST $config_ru $unicorn_config $lock_path" +TEST_RM_LIST="$TEST_RM_LIST $curl_out $curl_err" + +cat > $config_ru <<\EOF +use Rack::ContentLength +use Rack::ContentType +run lambda { |env| + sleep 1 + [ 200, {}, [ Thread.current.inspect << "\n" ] ] +} +EOF + +nr_client=30 +nr_thread=10 + +cat > $unicorn_config <> $curl_out 2>> $curl_err ) & +done +wait +echo elapsed=$(( $(date +%s) - $start )) + +kill $(cat $pid) + +! test -s $curl_err +test x"$(wc -l < $curl_out)" = x$nr_client + +nr=$(sort < $curl_out | uniq | wc -l) + +test "$nr" -le $nr_thread +test "$nr" -gt 1 diff --git a/t/t3000-revactor-basic.sh b/t/t3000-revactor-basic.sh new file mode 100755 index 0000000..163e0db --- /dev/null +++ b/t/t3000-revactor-basic.sh @@ -0,0 +1,52 @@ +#!/bin/sh +. ./test-lib.sh +require_revactor + +eval $(unused_listen) +config_ru=$(mktemp -t rainbows.$$.XXXXXXXX.config.ru) +unicorn_config=$(mktemp -t rainbows.$$.XXXXXXXX.unicorn.rb) +curl_out=$(mktemp -t rainbows.$$.XXXXXXXX.curl.out) +curl_err=$(mktemp -t rainbows.$$.XXXXXXXX.curl.err) +pid=$(mktemp -t rainbows.$$.XXXXXXXX.pid) +TEST_RM_LIST="$TEST_RM_LIST $config_ru $unicorn_config $lock_path" +TEST_RM_LIST="$TEST_RM_LIST $curl_out $curl_err" + +cat > $config_ru <<\EOF +use Rack::ContentLength +use Rack::ContentType +run lambda { |env| + Actor.sleep 1 + [ 200, {}, [ Thread.current.inspect << "\n" ] ] +} +EOF + +nr_client=30 +nr_actor=10 + +cat > $unicorn_config <> $curl_out 2>> $curl_err ) & +done +wait +echo elapsed=$(( $(date +%s) - $start )) + +kill $(cat $pid) + +! test -s $curl_err +test x"$(wc -l < $curl_out)" = x$nr_client +nr=$(sort < $curl_out | uniq | wc -l) + +test "$nr" -eq 1 diff --git a/t/test-lib.sh b/t/test-lib.sh new file mode 100644 index 0000000..9e286bc --- /dev/null +++ b/t/test-lib.sh @@ -0,0 +1,41 @@ +#!/bin/sh +# Copyright (c) 2009 Eric Wong +set -e +set -u +T=$(basename $0) +ruby="${ruby-ruby}" + +# ensure a sane environment +TZ=UTC LC_ALL=C LANG=C +export LANG LC_ALL TZ +unset CDPATH + +die () { + echo >&2 "$@" + exit 1 +} + +TEST_RM_LIST="" +trap 'rm -f $TEST_RM_LIST' 0 +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 +} + +require_revactor () { + if ! $ruby -rrevactor -e "puts Revactor::VERSION" >/dev/null 2>&1 + then + echo >&2 "skipping $T since we don't have Revactor" + exit 0 + fi +} -- cgit v1.2.3-24-ge0c7