r1529 - in trunk/varnish-tools/regress: . lib/Varnish lib/Varnish/Test

knutroy at projects.linpro.no knutroy at projects.linpro.no
Fri Jun 15 19:04:17 CEST 2007


Author: knutroy
Date: 2007-06-15 19:04:16 +0200 (Fri, 15 Jun 2007)
New Revision: 1529

Removed:
   trunk/varnish-tools/regress/lib/Varnish/Test/Logger.pm
Modified:
   trunk/varnish-tools/regress/TODO
   trunk/varnish-tools/regress/lib/Varnish/Test.pm
   trunk/varnish-tools/regress/lib/Varnish/Test/Case.pm
   trunk/varnish-tools/regress/lib/Varnish/Test/Client.pm
   trunk/varnish-tools/regress/lib/Varnish/Test/Engine.pm
   trunk/varnish-tools/regress/lib/Varnish/Test/Server.pm
   trunk/varnish-tools/regress/lib/Varnish/Test/Varnish.pm
Log:
Added some documentation.


Modified: trunk/varnish-tools/regress/TODO
===================================================================
--- trunk/varnish-tools/regress/TODO	2007-06-15 12:26:56 UTC (rev 1528)
+++ trunk/varnish-tools/regress/TODO	2007-06-15 17:04:16 UTC (rev 1529)
@@ -1,3 +1,2 @@
-* Ticket 55.
 * Completely POD-ify Perl-code.
 * Detect and act upon unexpected death of Varnish grandchild process.

Modified: trunk/varnish-tools/regress/lib/Varnish/Test/Case.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test/Case.pm	2007-06-15 12:26:56 UTC (rev 1528)
+++ trunk/varnish-tools/regress/lib/Varnish/Test/Case.pm	2007-06-15 17:04:16 UTC (rev 1529)
@@ -28,12 +28,27 @@
 # $Id$
 #
 
+=head1 NAME
+
+Varnish::Test::Case - test-case superclass
+
+=head1 DESCRIPTION
+
+Varnish::Test::Case is meant to be the superclass of specific
+test-case clases. It provides functionality to run a number of tests
+defined in methods whose names start with "test", as well as keeping
+track of the number of successful or failed tests.
+
+It also provides default event handlers for "ev_client_response" and
+"ev_client_timeout", which are standard for most test-cases.
+
+=cut
+
 package Varnish::Test::Case;
 
 use strict;
 
-use Varnish::Test::Logger;
-
+use Varnish::Test::Client;
 use HTTP::Request;
 use HTTP::Response;
 use Time::HiRes qw(gettimeofday tv_interval);

Modified: trunk/varnish-tools/regress/lib/Varnish/Test/Client.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test/Client.pm	2007-06-15 12:26:56 UTC (rev 1528)
+++ trunk/varnish-tools/regress/lib/Varnish/Test/Client.pm	2007-06-15 17:04:16 UTC (rev 1529)
@@ -28,6 +28,17 @@
 # $Id$
 #
 
+=head1 NAME
+
+Varnish::Test::Client - HTTP-client emulator
+
+=head1 DESCRIPTION
+
+Varnish::Test::Client objects have the capability of establishing HTTP
+connections, sending requests and receiving responses.
+
+=cut
+
 package Varnish::Test::Client;
 
 use strict;
@@ -86,7 +97,18 @@
 sub mux_input($$$$) {
     my ($self, $mux, $fh, $data) = @_;
 
+    # Iterate through the input buffer ($$data) and identify HTTP
+    # messages, one per iteration. Break out of the loop when there
+    # are no complete HTTP messages left in the buffer, and let
+    # whatever data remains stay in the buffer, as we will get a new
+    # chance to parse it next time we get more data ("mux_input") or
+    # if connection is closed ("mux_eof").
+
     while ($$data =~ /\n\r?\n/) {
+	# If we find a double (CR)LF in the input data, we have at
+	# least a complete header section of a message, so look for
+	# content-length and decide what to do.
+
 	my $response = HTTP::Response->parse($$data);
 	my $content_length = $response->content_length;
 
@@ -94,25 +116,47 @@
 	    my $content_ref = $response->content_ref;
 	    my $data_length = length($$content_ref);
 	    if ($data_length == $content_length) {
+		# We found exactly content-length amount of data, so
+		# empty input buffer and send response to event
+		# handling.
 		$$data = '';
 		$self->got_response($response);
 	    }
 	    elsif ($data_length < $content_length) {
+		# We only received the first part of an HTTP message,
+		# so break out of loop and wait for more.
 		$self->log(sprintf('Partial response. Bytes in body: %d received, %d expected, %d remaining',
 				   $data_length, $content_length, $content_length - $data_length));
 		last;
 	    }
 	    else {
+		# We have more than content-length data, which means
+		# more than just one HTTP message. The extra data
+		# (beyond content-length) is now at the end of
+		# $$content_ref, so move it back to the input buffer
+		# so we can parse it on the next iteration. Note that
+		# this "substr" also removes this data from
+		# $$content_ref (the message body of $response
+		# itself).
 		$$data = substr($$content_ref, $content_length,
 				$data_length - $content_length, '');
+
+		# Send response to event handling.
 		$self->got_response($response);
 	    }
 	}
 	else {
+	    # There is no content-length among the headers, so break
+	    # out of loop and wait for EOF, in which case mux_eof will
+	    # reparse the input buffer as a HTTP message and send it
+	    # to event handling from there.
 	    $self->log('Partial response. Content-Length unknown. Expecting CLOSE as end-of-response.');
 	    last;
 	}
     }
+
+    # At this point, what remains in the input buffer is either
+    # nothing at all or a partial HTTP message.
 }
 
 sub mux_eof($$$$) {

Modified: trunk/varnish-tools/regress/lib/Varnish/Test/Engine.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test/Engine.pm	2007-06-15 12:26:56 UTC (rev 1528)
+++ trunk/varnish-tools/regress/lib/Varnish/Test/Engine.pm	2007-06-15 17:04:16 UTC (rev 1529)
@@ -28,13 +28,29 @@
 # $Id$
 #
 
+=head1 NAME
+
+Varnish::Test::Engine - select-loop wrapper and event dispatcher
+
+=head1 DESCRIPTION
+
+Varnish::Test::Engine is primarily a wrapper around a
+IO::Multiplex-based select-loop which monitors activity on
+client-side, server-side and Varnish's I/O-channels. On startup, it
+automatically creates an associated Server object and a Varnish
+objects whoses sockets/filehandles are registered in the
+IO::Multiplex-object.
+
+Additionally, event dispatching is performed by the AUTOLOAD method.
+
+=cut
+
 package Varnish::Test::Engine;
 
 use strict;
 
 use Varnish::Test::Server;
 use Varnish::Test::Varnish;
-use Varnish::Test::Client;
 use IO::Multiplex;
 
 sub new($$;%) {
@@ -68,32 +84,53 @@
 sub run_loop($@) {
     my ($self, @wait_for) = @_;
 
+    # Sanity-check to help the novice test-case writer.
     die "Engine::run_loop: Already inside select-loop. Your code is buggy.\n"
 	if exists($self->{'in_loop'});
 
+    # We need to wait for at least one event.
     die "Engine::run_loop: No events to wait for.\n"
 	if @wait_for == 0;
 
+    # Check the queue for pending events which occurred between the
+    # last pausing event and the time the loop actually paused. If we
+    # are waiting for any of these events (which already occurred),
+    # return the first one we find immediately.
     while (@{$self->{'pending'}} > 0) {
 	my ($event, @args) = @{shift @{$self->{'pending'}}};
 	return ($event, @args) if grep({ $_ eq $event } @wait_for);
     }
 
+    # At this point, the queue of pending events is always empty.
+    # Prepare and run IO::Multiplex::loop.
+
     $self->{'wait_for'} = \@wait_for;
     $self->{'in_loop'} = 1;
     $self->{'mux'}->loop;
     delete $self->{'in_loop'};
     delete $self->{'wait_for'};
 
+    # Loop has now been paused due to the occurrence of an event we
+    # were waiting for. This event is always found in the front of the
+    # pending events queue at this point, so return it.
     return @{shift @{$self->{'pending'}}} if @{$self->{'pending'}} > 0;
+
+    # Hm... we should usually not reach this point. The pending queue
+    # is empty. Either someone (erroneously) requested a loop pause by
+    # calling IO::Multiplex::endloop and forgot to put any event in
+    # the queue, or the loop ended itself because all registered
+    # filehandles/sockets closed.
     return undef;
 }
 
 sub shutdown($) {
     my ($self) = @_;
 
+    # Shutdown varnish and server.
     $self->{'varnish'}->shutdown if defined $self->{'varnish'};
     $self->{'server'}->shutdown if defined $self->{'server'};
+
+    # Close any lingering sockets registered with IO::Multiplex.
     foreach my $fh ($self->{'mux'}->handles) {
 	$self->{'mux'}->close($fh);
     }
@@ -106,18 +143,32 @@
 
     return if $event eq 'DESTROY';
 
+    # For the sake of readability, we want all method names we handle
+    # to start with "ev_".
     die sprintf("Unknown method '%s'\n", $event)
 	unless $event =~ /^ev_(.*)$/;
 
     $self->log($self, 'ENG: ', sprintf('EVENT "%s"', $1));
 
+    # Check to see if the active case object defines an event handler
+    # for this event. If so, call it and bring the event arguments
+    # along. This will also replace @args, which is significant if
+    # this event will pause and return.
     @args = $self->{'case'}->$event(@args)
 	if (defined($self->{'case'}) and $self->{'case'}->can($event));
 
     if (@{$self->{'pending'}} > 0) {
-	push(@{$self->{'pending'}}, [ $event, @args ]);
+	# Pending event queue is NOT empty, meaning this is an event
+	# arriving after a pausing (wait_for) event, but before the
+	# pause is in effect. We queue this event unconditionally
+	# because it might be the one we are waiting for on the next
+	# call to run_loop.
+ 	push(@{$self->{'pending'}}, [ $event, @args ]);
     }
     elsif (grep({ $_ eq $event} @{$self->{'wait_for'}}) > 0) {
+	# Pending event queue is empty and this event is one of those
+	# we are waiting for, so put it in the front of the queue and
+	# signal loop pause by calling IO::Multiplex::endloop.
 	push(@{$self->{'pending'}}, [ $event, @args ]);
 	$self->{'mux'}->endloop;
     }

Deleted: trunk/varnish-tools/regress/lib/Varnish/Test/Logger.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test/Logger.pm	2007-06-15 12:26:56 UTC (rev 1528)
+++ trunk/varnish-tools/regress/lib/Varnish/Test/Logger.pm	2007-06-15 17:04:16 UTC (rev 1529)
@@ -1,55 +0,0 @@
-#!/usr/bin/perl -w
-#-
-# Copyright (c) 2006 Linpro AS
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-# 1. Redistributions of source code must retain the above copyright
-#    notice, this list of conditions and the following disclaimer
-#    in this position and unchanged.
-# 2. Redistributions in binary form must reproduce the above copyright
-#    notice, this list of conditions and the following disclaimer in the
-#    documentation and/or other materials provided with the distribution.
-#
-# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
-# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
-# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
-# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
-# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
-# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
-# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
-# SUCH DAMAGE.
-#
-# $Id$
-#
-
-package Varnish::Test::Logger;
-
-sub new($;$) {
-    my ($this, $prefix) =  @_;
-    my $class = ref($this) || $this;
-
-    my $self = bless({ 'prefix' => $prefix || '' }, $class);
-}
-
-sub write($$;$) {
-    my ($self, $data, $extra_prefix) = @_;
-
-    my $prefix = $self->{'prefix'};
-    $prefix .= ': ' . $extra_prefix if defined($extra_prefix);
-
-    if ($prefix) {
-	$data =~ s/^/$prefix: /gm;
-    }
-
-    $data =~ s/\n?$/\n/;
-
-    print STDERR $data;
-}
-
-1;

Modified: trunk/varnish-tools/regress/lib/Varnish/Test/Server.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test/Server.pm	2007-06-15 12:26:56 UTC (rev 1528)
+++ trunk/varnish-tools/regress/lib/Varnish/Test/Server.pm	2007-06-15 17:04:16 UTC (rev 1529)
@@ -28,6 +28,20 @@
 # $Id$
 #
 
+=head1 NAME
+
+Varnish::Test::Server - HTTP-server emulator
+
+=head1 DESCRIPTION
+
+A Varnish::Test::Server object has the capability of listening on a
+TCP socket, receiving HTTP requests and sending responses.
+
+Every established connection is handled by an associated object of
+type Varnish::Test::Server::Connection.
+
+=cut
+
 package Varnish::Test::Server;
 
 use strict;
@@ -127,7 +141,17 @@
 sub mux_input($$$$) {
     my ($self, $mux, $fh, $data) = @_;
 
+    # Iterate through the input buffer ($$data) and identify HTTP
+    # messages, one per iteration. Break out of the loop when there
+    # are no complete HTTP messages left in the buffer, and let
+    # whatever data remains stay in the buffer, as we will get a new
+    # chance to parse it next time we get more data ("mux_input").
+
     while ($$data =~ /\n\r?\n/) {
+	# If we find a double (CR)LF in the input data, we have at
+	# least a complete header section of a message, so look for
+	# content-length and decide what to do.
+
 	my $request = HTTP::Request->parse($$data);
 	my $content_ref = $request->content_ref;
 	my $content_length = $request->content_length;
@@ -135,19 +159,38 @@
 	if (defined($content_length)) {
 	    my $data_length = length($$content_ref);
 	    if ($data_length == $content_length) {
+		# We found exactly content-length amount of data, so
+		# empty input buffer and send request to event
+		# handling.
 		$$data = '';
 		$self->{'server'}->got_request($self, $request);
 	    }
 	    elsif ($data_length < $content_length) {
+		# We only received the first part of an HTTP message,
+		# so break out of loop and wait for more.
 		last;
 	    }
 	    else {
+		# We have more than content-length data, which means
+		# more than just one HTTP message. The extra data
+		# (beyond content-length) is now at the end of
+		# $$content_ref, so move it back to the input buffer
+		# so we can parse it on the next iteration. Note that
+		# this "substr" also removes this data from
+		# $$content_ref (the message body of $request itself).
 		$$data = substr($$content_ref, $content_length,
 				$data_length - $content_length, '');
+		# Send request to event handling.
 		$self->{'server'}->got_request($self, $request);
 	    }
 	}
 	else {
+	    # HTTP requests without a content-length has no body by
+	    # definition, so whatever was parsed as content must be
+	    # the start of another request. Hence, move this back to
+	    # input buffer and empty the body of this $request. Then,
+	    # send $request to event handling.
+
 	    $$data = $$content_ref;
 	    $$content_ref = '';
 	    $self->{'server'}->got_request($self, $request);
@@ -158,6 +201,10 @@
 sub mux_eof($$$$) {
     my ($self, $mux, $fh, $data) = @_;
 
+    # On server side, HTTP does not use EOF from client to signal end
+    # of request, so if there is anything left in input buffer, it
+    # must be incomplete because "mux_input" left it there.
+
     die "Junk or incomplete request\n"
 	unless $$data eq '';
 }

Modified: trunk/varnish-tools/regress/lib/Varnish/Test/Varnish.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test/Varnish.pm	2007-06-15 12:26:56 UTC (rev 1528)
+++ trunk/varnish-tools/regress/lib/Varnish/Test/Varnish.pm	2007-06-15 17:04:16 UTC (rev 1529)
@@ -28,14 +28,27 @@
 # $Id$
 #
 
+=head1 NAME
+
+Varnish::Test::Varnish - Varnish child-process controller
+
+=head1 DESCRIPTION
+
+A Varnish::Test::Varnish object is used to fork off a Varnish child
+process and control traffic going into and coming out of the Varnish
+(management process) command-line interface (CLI).
+
+Various events are generated when certain strings are identified in
+the output from the CLI.
+
+=cut
+
 package Varnish::Test::Varnish;
 
 use strict;
 
 use Socket;
 
-use Varnish::Test::Logger;
-
 sub new($$;$) {
     my ($this, $engine, $attrs) =  @_;
     my $class = ref($this) || $this;
@@ -44,6 +57,9 @@
 		       'mux' => $engine->{'mux'},
 		       'state' => 'init' }, $class);
 
+    # Create pipes (actually socket pairs) for communication between
+    # parent and child.
+
     socketpair(STDIN_READ, STDIN_WRITE, AF_UNIX, SOCK_STREAM, PF_UNSPEC);
     shutdown(STDIN_READ, 1);
     shutdown(STDIN_WRITE, 0);
@@ -54,7 +70,8 @@
     shutdown(STDERR_READ, 1);
     shutdown(STDERR_WRITE, 0);
 
-    delete $SIG{CHLD};
+    # Ignore SIGCHLD.
+    $SIG{CHLD} = 'IGNORE';
 
     my $pid = fork;
     die "fork(): $!\n"
@@ -67,6 +84,9 @@
 	close STDOUT_READ;
 	close STDERR_READ;
 
+	# dup2(2) the I/O-channels to std{in,out,err} and close the
+	# original file handles before transforming into Varnish.
+
 	open STDIN, '<&', \*STDIN_READ;
 	close STDIN_READ;
 	open STDOUT, '>&', \*STDOUT_WRITE;
@@ -80,14 +100,17 @@
 
 	print STDERR sprintf("Starting Varnish with options: %s\n", join(' ', @opts));
 
+	# Unset ignoring of SIGCHLD, so Varnish will get signals from
+	# its children.
+
+	delete $SIG{CHLD};
+
+	# Transform into Varnish. Goodbye Perl-code!
 	exec('varnishd', @opts);
 	exit(1);
     }
     else {
 	# Parent
-
-	$SIG{CHLD} = 'IGNORE';
-
 	$self->log('PID: ' . $pid);
 
 	close STDIN_READ;
@@ -99,6 +122,9 @@
 	$self->{'stdout'} = \*STDOUT_READ;
 	$self->{'stderr'} = \*STDERR_READ;
 
+	# Register the Varnish I/O-channels with the IO::Multiplex
+	# loop object.
+
 	$self->{'mux'}->add($self->{'stdin'});
 	$self->{'mux'}->set_callback_object($self, $self->{'stdin'});
 	$self->{'mux'}->add($self->{'stdout'});

Modified: trunk/varnish-tools/regress/lib/Varnish/Test.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test.pm	2007-06-15 12:26:56 UTC (rev 1528)
+++ trunk/varnish-tools/regress/lib/Varnish/Test.pm	2007-06-15 17:04:16 UTC (rev 1529)
@@ -38,31 +38,66 @@
 process and then communicating with this process as both client and
 server.
 
+ +---------------------------------------------------------+
+ |                     TEST FRAMEWORK                      |
+ |                                                         |
+ |                       Controller                        |
+ |          +-----------------------------------+          |
+ |          |               | C ^               |          |
+ |          | configuration | L | status        |          |
+ |          |               v I |               |          |
+ |          |  requests  +---------+  requests  |          |
+ |          | =========> |         | =========> |          |
+ | Client   |    HTTP    | VARNISH |    HTTP    | Server   |
+ | emulator | <========= |         | <========= | emulator |
+ |          |  responses +---------+  responses |          |
+ +----------+                                   +----------+
+
 =head1 STRUCTURE
 
-When regressions tests start, an instance of Varnish is forked off as
-a child process, and its I/O channels (std{in,out,err}) are controlled
-by the parent process which also performs the test by playing the role
+When regression tests start, an instance of Varnish is forked off as a
+child process, and its I/O channels (std{in,out,err} which are
+connected to the command-line interface of Varnish) are controlled by
+the parent process which also performs the tests by playing the role
 of both HTTP client and server.
 
 A single select(2)-driven loop is used to handle all activity on both
 server and client side, as well on Varnish's I/O-channels. This is
 done using IO::Multiplex.
 
-As a result of using a select-loop, the framework has an event-driven
-design in order to cope with unpredictable sequence of processing on
-either server og client side. To drive a test-case forward, the
-select-loop is paused when certain events occur, and control returns
-to the "main program" which can then inspect the situation. This
-results in certain structural constraints. It is essential to be aware
-of whether a piece of code is going to run inside or outside the
-select-loop.
+As a result of using a select-loop (as opposed to a multi-threaded or
+multi-process approach), the framework has an event-driven design in
+order to cope with the unpredictable sequence of I/O on server or
+client side (or Varnish's I/O-channels for that matter) . To drive a
+test-case forward, the select-loop is paused when certain events
+occur, and control returns to the "main program" which can then
+inspect the situation. This results in certain structural constraints,
+and it is essential to be aware of whether a piece of code is going to
+run inside (event handler) or outside (main program) the select-loop.
 
-The framework uses Perl objects to represent instances of servers and
-clients as well as the Varnish instance itself. In addition, there is
-an "engine" object which propagates events and controls the program
-flow related to the select-loop.
+The framework uses Perl objects to represent instances of servers
+(Varnish::Test::Server) and clients (Varnish::Test::Client) as well as
+the Varnish instance itself (Varnish::Test::Varnish). In addition,
+there is an engine object (Varnish::Test::Engine) which dispatches
+events and controls the program flow related to the select-loop.
+Futhermore, each test case is represented by an object
+(Varnish::Test::Case subclass). HTTP requests and responses are
+represented by objects of HTTP::Request and HTTP::Response,
+respectively. Finally, there is an overall test-case controller object
+(Varnish::Test) which accumulates test-case results.
 
+=head1 EVENT PROCESSING
+
+Events typically occur in the call-back routines (mux_*) of client,
+server, and Varnish objects. An event is created by calling an ev_*
+method of the engine object. These calls are handled by Perl's
+AUTOLOAD mechanism since Engine does not define any ev_* methods
+explicitly. The AUTOLOAD routine works as the event dispatcher by
+looking for an event handler in the currently running test-case
+object, and also determines whether the event being processed is
+supposed to pause the select-loop and return control back to the main
+program.
+
 =cut
 
 package Varnish::Test;




More information about the varnish-commit mailing list