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

knutroy at projects.linpro.no knutroy at projects.linpro.no
Fri Feb 16 14:26:52 CET 2007


Author: knutroy
Date: 2007-02-16 14:26:52 +0100 (Fri, 16 Feb 2007)
New Revision: 1242

Added:
   trunk/varnish-tools/regress/README
   trunk/varnish-tools/regress/TODO
Modified:
   trunk/varnish-tools/regress/lib/Varnish/Test.pm
   trunk/varnish-tools/regress/lib/Varnish/Test/Accelerator.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/Expression.pm
   trunk/varnish-tools/regress/lib/Varnish/Test/Invocation.pm
   trunk/varnish-tools/regress/lib/Varnish/Test/Server.pm
   trunk/varnish-tools/regress/lib/Varnish/Test/Statement.pm
   trunk/varnish-tools/regress/test1
Log:
Updated regression test framework so that it runs "test1" sample code.
See TODO-file for a (non-exhaustive) list of what remains to be done.


Added: trunk/varnish-tools/regress/README
===================================================================
--- trunk/varnish-tools/regress/README	2007-02-06 21:55:03 UTC (rev 1241)
+++ trunk/varnish-tools/regress/README	2007-02-16 13:26:52 UTC (rev 1242)
@@ -0,0 +1,60 @@
+VARNISH REGRESSION TEST FRAMEWORK
+
+This is a regression test framework written in Perl. It is being
+tailored to the needs of the Varnish HTTP accelerator.
+
+The framework is based on interpreting a mini-language designed for
+this specific purpose. The mini-language expresses test case setups
+and conditions to be tested.
+
+The Perl-based interpreter sets up the run-time environment and
+executes a "program" written in this mini-language.
+
+The mini-language's grammar can be found in lib/Varnish/Test/Parser.pm
+which utilizes the Parse::RecDescent CPAN-module.
+
+The interpreter creates a run-time environment consisting of simulated
+clients and servers which live in the main process. In addition, it
+forks off a Varnish sub-process through which the clients and servers
+send HTTP-traffic. The main process uses a global select(2)-based loop
+(using IO::Multiplex) to which all the simulated clients and servers
+must relate. Hence, no threading is needed, but disciplined use
+sockets (to avoid blocking and other trouble) is required.
+
+When the mini-language is parsed, a tree of Perl-objects is created.
+There are classes representing:
+
+  * a server (Varnish::Test::Server)
+  * a client (Varnish::Test::Client)
+  * an accelerator/Varnish instance (Varnish::Test::Accelerator)
+  * a test-case (Varnish::Test::Case)
+  * a statement (Varnish::Test::Statement)
+  * an expression (Varnish::Test::Expression)
+  * a function invocation (Varnish::Test::Invocation)
+
+These classes share some properties which are found
+Varnish::Test::Object, most notably the ability to be "executed" and
+temporarily paused when the IO::Multiplex-loop needs to transfers
+control to another object.
+
+To keep track of execution, all objects have an attribute, "finished",
+which tells its parent whether execution has already terminated. In
+addition an attribute "return" is used to hold any return value should
+the object have a sensible return value to offer (which is the true
+for statements, expressions, and function invocations). Before
+"finished" is set to true, "return" has no meaning.
+
+The parent will execute its children sequentially, in the same order
+as they are defined in the source code.
+
+However, some objects get control back after they are "finished". This
+is the case for server objects when they serve requests, which happens
+asynchronously to ordinary execution and is orchestrated by the
+IO::Multiplex-loop. When the server object has handled the request,
+control returns to the original point of execution. Finding that point
+is done by skipping past all objects whose "finished"-attribute is
+true.
+
+Finally, the notion of scope and variables is taken care of by
+functionality provided in the super-class Varnish::Test::Context from
+which Varnish::Test::Object inherits.

Added: trunk/varnish-tools/regress/TODO
===================================================================
--- trunk/varnish-tools/regress/TODO	2007-02-06 21:55:03 UTC (rev 1241)
+++ trunk/varnish-tools/regress/TODO	2007-02-16 13:26:52 UTC (rev 1242)
@@ -0,0 +1,23 @@
+* Revise class hierarchy, possibly switching around
+  Varnish::Test::Context and Varnish::Test::Object since we might like
+  to inherit the properties of Object without getting the properties
+  of Context, in classes like Varnish::Test::Statement,
+  Varnish::Test::Expression, and Varnish::Test::Invocation.
+
+* Actually handle HTTP by utilizing Varnish::Test::Message (and
+  the sub-classes Varnish::Test::Request and Varnish::Test::Response)
+  as variables that live inside server and client objects.
+
+* Extend the language (syntax and semantics), to make it more
+  expressive and useful.
+
+* POD-ify Perl-code.
+
+* Fix IO::Multiplex-related warnings:
+
+  · Use of uninitialized value in unpack at /usr/share/perl5/IO/Multiplex.pm line 351.
+    Use of uninitialized value in numeric eq (==) at /usr/share/perl5/IO/Multiplex.pm line 351.
+
+  · Use of freed value in iteration at /usr/share/perl5/IO/Multiplex.pm line 721.
+
+  (Is this IO::Multiplex' or our fault?)

Modified: trunk/varnish-tools/regress/lib/Varnish/Test/Accelerator.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test/Accelerator.pm	2007-02-06 21:55:03 UTC (rev 1241)
+++ trunk/varnish-tools/regress/lib/Varnish/Test/Accelerator.pm	2007-02-16 13:26:52 UTC (rev 1242)
@@ -32,16 +32,21 @@
 
 use strict;
 use base 'Varnish::Test::Object';
+use IO::Pipe;
 use POSIX;
 
 sub _init($) {
     my $self = shift;
 
+    &Varnish::Test::Object::_init($self);
+
     # Default address / port
     $self->vars->{'address'} = 'localhost';
     $self->vars->{'port'} = '8001';
 }
 
+use Data::Dumper;
+
 sub start($) {
     my $self = shift;
 
@@ -50,59 +55,129 @@
      $backend->isa('Varnish::Test::Server'))
 	or die("invalid server\n");
 
-    my ($stdinx, $stdin) = POSIX::pipe()
-	or die("pipe(): $!\n");
-    my ($stdout, $stdoutx) = POSIX::pipe()
-	or die("pipe(): $!\n");
-    my ($stderr, $stderrx) = POSIX::pipe()
-	or die("pipe(): $!\n");
+    my $stdin = new IO::Pipe;
+    my $stdout = new IO::Pipe;
+    my $stderr = new IO::Pipe;
     my $pid = fork();
     if (!defined($pid)) {
 	# fail
 	die("fork(): $!\n");
     } elsif ($pid == 0) {
 	# child
-	POSIX::dup2($stdinx, 0);
-	POSIX::close($stdin);
-	POSIX::close($stdinx);
-	POSIX::dup2($stdoutx, 1);
-	POSIX::close($stdout);
-	POSIX::close($stdoutx);
-	POSIX::dup2($stderrx, 2);
-	POSIX::close($stderr);
-	POSIX::close($stderrx);
+	$stdin->reader;
+	$stdout->writer;
+	$stderr->writer;
+
+	POSIX::dup2($stdin->fileno, 0);
+	$stdin->close;
+	POSIX::dup2($stdout->fileno, 1);
+	$stdout->close;
+	POSIX::dup2($stderr->fileno, 2);
+	$stderr->close;
 	# XXX must be in path
+	$ENV{'PATH'} = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin';
 	exec('varnishd',
 	     '-d', '-d',
+	     '-a', $self->get('address') . ":" . $self->get('port'),
 	     '-b', $backend->get('address') . ":" . $backend->get('port'));
 	exit(1);
     }
     # parent
+
+    $stdin->writer;
+    $stdout->reader;
+    $stderr->reader;
+
     $self->{'pid'} = $pid;
     $self->{'stdin'} = $stdin;
-    POSIX::close($stdinx);
     $self->{'stdout'} = $stdout;
-    POSIX::close($stdoutx);
     $self->{'stderr'} = $stderr;
-    POSIX::close($stderrx);
+
+    # IO::Multiplex is going to issue some warnings here, because it
+    # does not handle non-socket file descriptors gently.
+
+    my $mux = $self->get_mux;
+    $mux->add($stdin);
+    $mux->set_callback_object($self, $stdin);
+    $mux->add($stdout);
+    $mux->set_callback_object($self, $stdout);
+    $mux->add($stderr);
+    $mux->set_callback_object($self, $stderr);
+
+    if ($self->has('vcl')) {
+	my $vcl = $self->get('vcl');
+	$vcl =~ s/\n/ /g;
+	$mux->write($stdin, "vcl.inline main " . $vcl . "\n");
+    }
 }
 
 sub stop($) {
     my $self = shift;
 
-    POSIX::close($self->{'stdin'})
-	if ($self->{'stdin'});
-    POSIX::close($self->{'stdout'})
-	if ($self->{'stdout'});
-    POSIX::close($self->{'stderr'})
-	if ($self->{'stderr'});
+    my $mux = $self->get_mux;
+
+    foreach my $k ('stdin', 'stdout', 'stderr') {
+	if (defined($self->{$k})) {
+	    $mux->close($self->{$k});
+	    delete $self->{$k};
+	}
+    }
     sleep(1);
     kill(15, $self->{'pid'})
 	if ($self->{'pid'});
-    delete($self->{'stdin'});
-    delete($self->{'stdout'});
-    delete($self->{'stderr'});
     delete($self->{'pid'});
 }
 
+sub run($) {
+    my $self = shift;
+
+    return if $self->{'finished'} or defined($self->{'pid'});
+
+    &Varnish::Test::Object::run($self);
+
+    $self->start;
+    $self->{'finished'} = 0;
+}
+
+sub shutdown($) {
+    my $self = shift;
+
+    $self->stop;
+}
+
+sub mux_input($$$$) {
+    my $self = shift;
+    my $mux = shift;
+    my $fh = shift;
+    my $data = shift;
+
+    print STDERR $$data;
+
+    if ($$data =~ /vcl.inline/) {
+	$mux->write($self->{'stdin'}, "start\n");
+    }
+
+    my $started = ($$data =~ /Child starts/);
+    $$data = '';
+
+    if ($started) {
+	$self->{'finished'} = 1;
+	$self->super_run;
+    }
+}
+
+sub mux_eof($$$$) {
+    my $self = shift;
+    my $mux = shift;
+    my $fh = shift;
+    my $data = shift;
+
+    $mux->close($fh);
+    foreach my $k ('stdin', 'stdout', 'stderr') {
+	if (defined($self->{$k}) && $self->{$k} == $fh) {
+	    delete $self->{$k};
+	}
+    }
+}
+
 1;

Modified: trunk/varnish-tools/regress/lib/Varnish/Test/Case.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test/Case.pm	2007-02-06 21:55:03 UTC (rev 1241)
+++ trunk/varnish-tools/regress/lib/Varnish/Test/Case.pm	2007-02-16 13:26:52 UTC (rev 1242)
@@ -33,12 +33,43 @@
 use strict;
 use base 'Varnish::Test::Object';
 
+sub _init($) {
+    my $self = shift;
+
+    &Varnish::Test::Object::_init($self);
+
+    $self->set('assert', \&assert);
+}
+
 sub run($) {
     my $self = shift;
 
-    print "Running case \"$self->{name}\"...\n";
+    if (!defined($self->{'started'})) {
+	print "Start of CASE \"$self->{name}\"...\n";
+	$self->{'started'} = 1;
+    }
 
     &Varnish::Test::Object::run($self);
+
+    if ($self->{'finished'}) {
+	print "End of CASE \"$self->{name}\".\n";
+    }
 }
 
+sub assert($$) {
+    my $self = shift;
+    my $invocation = shift;
+
+    my $bool = $invocation->{'args'}[0]->{'return'};
+
+    if (!$bool) {
+	print "  ASSERTION DOES NOT HOLD.\n";
+    }
+    else {
+	print "  Assertion holds.\n";
+    }
+
+    $invocation->{'finished'} = 1;
+}
+
 1;

Modified: trunk/varnish-tools/regress/lib/Varnish/Test/Client.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test/Client.pm	2007-02-06 21:55:03 UTC (rev 1241)
+++ trunk/varnish-tools/regress/lib/Varnish/Test/Client.pm	2007-02-16 13:26:52 UTC (rev 1242)
@@ -38,6 +38,8 @@
 sub _init($) {
     my $self = shift;
 
+    &Varnish::Test::Object::_init($self);
+
     $self->set('protocol', '1.1');
     $self->set('request', \&request);
 }
@@ -66,8 +68,7 @@
     $mux->add($fh);
     $mux->set_callback_object($self, $fh);
 
-    $mux->write($fh, "Hello\r\n");
-    print "Client sent: Hello\n";
+    $mux->write($fh, "GET / HTTP/" . eval($self->get('protocol')) . "\r\n\r\n");
 
     $self->{'request'} = $invocation;
 }
@@ -77,9 +78,16 @@
     my $mux = shift;
     my $fh = shift;
     my $data = shift;
+    my $response = new Varnish::Test::Context('response', $self);
 
     $self->{'request'}->{'return'} = $$data;
-    print "Client got: $$data";
+    if ($$data =~ 'HTTP/1.1') {
+	$response->set('protocol', '1.1');
+    }
+    else {
+	$response->set('protocol', '1.0');
+    }
+    print STDERR "Client got: $$data";
     $$data = "";
     $self->{'request'}->{'finished'} = 1;
     delete $self->{'request'};

Modified: trunk/varnish-tools/regress/lib/Varnish/Test/Expression.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test/Expression.pm	2007-02-06 21:55:03 UTC (rev 1241)
+++ trunk/varnish-tools/regress/lib/Varnish/Test/Expression.pm	2007-02-16 13:26:52 UTC (rev 1242)
@@ -73,9 +73,11 @@
 
     &Varnish::Test::Object::run($self);
 
+    my $expr = '';
+    my $seen_string = 0;
+    my $relational = 0;
+
     if ($self->{'finished'} && defined($self->{'terms'})) {
-	my $expr = '';
-	my $return_as_string = 0;
 
 	foreach my $term (@{$self->{'terms'}}) {
 	    my $term_value;
@@ -89,6 +91,20 @@
 		}
 	    }
 	    else {
+		if ($term eq '==' || $term eq '!='
+		    || $term eq '<=' || $term eq '>='
+		    || $term eq '<' || $term eq '>') {
+		    $relational = 1;
+
+		    if ($seen_string) {
+			if ($term eq '==') {
+			    $term = 'eq';
+			}
+			elsif ($term eq '!=') {
+			    $term = 'ne';
+			}
+		    }
+		}
 		$term_value = $term;
 	    }
 
@@ -98,12 +114,12 @@
 		    return;
 		}
 		else {
-		    die "Found object/context reference in complex expression.";
+		    $term_value = '"' . $term_value . '"';
 		}
 	    }
 
 	    if ($term_value =~ /^".*"$/s) {
-		$return_as_string = 1;
+		$seen_string = 1;
 	    }
 
 	    $expr .= $term_value;
@@ -111,9 +127,11 @@
 
 	($expr) = $expr =~ /(.*)/s;
 
+	# print STDERR "Evaling: $expr\n";
+
 	$expr = eval $expr;
 
-	if ($return_as_string) {
+	if ($seen_string && !$relational) {
 	    $expr = '"' . $expr . '"';
 	}
 

Modified: trunk/varnish-tools/regress/lib/Varnish/Test/Invocation.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test/Invocation.pm	2007-02-06 21:55:03 UTC (rev 1241)
+++ trunk/varnish-tools/regress/lib/Varnish/Test/Invocation.pm	2007-02-16 13:26:52 UTC (rev 1242)
@@ -60,7 +60,7 @@
 	if (!$self->{'in_call'}) {
 	    $self->{'in_call'} = 1;
 	    my ($func_ptr, $func_context) = $self->{'func_id'}->get_function($self);
-	    print "Calling " . $self->{'func_id'}->as_string, "\n";
+	    # print STDERR "Calling " . $self->{'func_id'}->as_string, "\n";
 	    &$func_ptr($func_context, $self);
 	}
     }

Modified: trunk/varnish-tools/regress/lib/Varnish/Test/Server.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test/Server.pm	2007-02-06 21:55:03 UTC (rev 1241)
+++ trunk/varnish-tools/regress/lib/Varnish/Test/Server.pm	2007-02-16 13:26:52 UTC (rev 1242)
@@ -37,6 +37,8 @@
 sub _init($) {
     my $self = shift;
 
+    &Varnish::Test::Object::_init($self);
+
     $self->set('address', 'localhost');
     $self->set('port', '9001');
 }
@@ -81,10 +83,14 @@
     my $fh = shift;
     my $data = shift;
 
-    print "Server got: $$data";
-    $$data = "";
-    $mux->write($fh, "HTTP/1.1 200 OK\r\n");
-    print "Server sent: HTTP/1.1 200 OK\n";
+    $$data = ""; # Pretend we read the data.
+
+    my $response = "HTTP/" . eval($self->get('protocol')) . " 200 OK\r\n"
+	. "Content-Type: text/plain; charset=utf-8\r\n\r\n"
+	. eval($self->get('data')) . "\n";
+
+    $mux->write($fh, $response);
+    print STDERR "Server sent: " . $response;
     $mux->shutdown($fh, 1);
 }
 

Modified: trunk/varnish-tools/regress/lib/Varnish/Test/Statement.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test/Statement.pm	2007-02-06 21:55:03 UTC (rev 1241)
+++ trunk/varnish-tools/regress/lib/Varnish/Test/Statement.pm	2007-02-16 13:26:52 UTC (rev 1242)
@@ -53,6 +53,8 @@
     }
 }
 
+use Data::Dumper;
+
 sub run($$) {
     my $self = shift;
 
@@ -61,7 +63,7 @@
     &Varnish::Test::Object::run($self);
 
     if ($self->{'finished'}) {
-	$self->{'lhs'}->set_value($self, $self->{'return'});
+	$self->{'lhs'}->set_value($self->{'parent'}, $self->{'return'});
     }
 }
 

Modified: trunk/varnish-tools/regress/lib/Varnish/Test.pm
===================================================================
--- trunk/varnish-tools/regress/lib/Varnish/Test.pm	2007-02-06 21:55:03 UTC (rev 1241)
+++ trunk/varnish-tools/regress/lib/Varnish/Test.pm	2007-02-16 13:26:52 UTC (rev 1242)
@@ -77,9 +77,9 @@
 	die("Parsing error.");
     }
 
-    print "###### SYNTAX TREE BEGIN ######\n";
-    print Dumper $tree if defined($tree->{'body'});
-    print "###### SYNTAX TREE END ######\n";
+    print STDERR "###### SYNTAX TREE BEGIN ######\n";
+    print STDERR Dumper $tree if defined($tree->{'body'});
+    print STDERR "###### SYNTAX TREE END ######\n";
 
     $self->{'objects'} = [];
 
@@ -99,11 +99,11 @@
 
     while (!$self->{'finished'}) {
 	&Varnish::Test::Object::run($self);
-	print "Entering IO::Multiplex loop.\n";
+	print STDERR "Entering IO::Multiplex loop.\n";
 	$self->{'mux'}->loop;
     }
 
-    print "DONE.\n";
+    print STDERR "DONE.\n";
 }
 
 sub run($) {

Modified: trunk/varnish-tools/regress/test1
===================================================================
--- trunk/varnish-tools/regress/test1	2007-02-06 21:55:03 UTC (rev 1241)
+++ trunk/varnish-tools/regress/test1	2007-02-16 13:26:52 UTC (rev 1242)
@@ -21,8 +21,31 @@
 	comment = "client 1.0, server 1.0";
 	c1.protocol = "1.0";
 	s1.protocol = "1.0";
-	c1.request(s1, "http://www.example.com/");
-	c1.request(s1, "http://www.example.com/");
-	c1.request(s1, "http://www.example.com/");
+	c1.request(a1, "http://www.example.com/");
+	assert(c1.response.protocol == "1.0");
     }
+
+    case c10_s11 {
+	comment = "client 1.0, server 1.1";
+	c1.protocol = "1.0";
+	s1.protocol = "1.1";
+	c1.request(a1, "http://www.example.com/");
+	assert(c1.response.protocol == "1.0");
+    }
+
+    case c11_s10 {
+	comment = "client 1.1, server 1.0";
+	c1.protocol = "1.1";
+	s1.protocol = "1.0";
+	c1.request(a1, "http://www.example.com/");
+	assert(c1.response.protocol == "1.1");
+    }
+
+    case c11_s11 {
+	comment = "client 1.1, server 1.1";
+	c1.protocol = "1.1";
+	s1.protocol = "1.1";
+	c1.request(a1, "http://www.example.com/");
+	assert(c1.response.protocol == "1.1");
+    }
 }




More information about the varnish-commit mailing list