user/dev discussion of public-inbox itself
 help / color / mirror / code / Atom feed
Search results ordered by [date|relevance]  view[summary|nested|Atom feed]
thread overview below | download mbox.gz: |
* [PATCH 12/29] t/common: introduce run_script wrapper for t/cgi.t
  2019-11-15  9:50  7% [PATCH 00/29] speed up tests by preloading Eric Wong
@ 2019-11-15  9:50  6% ` Eric Wong
  0 siblings, 0 replies; 2+ results
From: Eric Wong @ 2019-11-15  9:50 UTC (permalink / raw)
  To: meta

This will give us a consistent interface for running
test scripts in more performant ways while still giving
us a consistent interface to recreate real-world behavior
via spawn() (fork + execve), if needed.

The default run_mode (1) is faster and can run within the test
process with some minor adjustments to our code to avoid global
state.

This avoids the significante overhead of Perl code loading,
parsing and compilation phases.
---
 t/cgi.t       |  16 ++-----
 t/common.perl | 125 +++++++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 129 insertions(+), 12 deletions(-)

diff --git a/t/cgi.t b/t/cgi.t
index 1b4b06cb..3c09ecd6 100644
--- a/t/cgi.t
+++ b/t/cgi.t
@@ -7,10 +7,7 @@ use warnings;
 use Test::More;
 use Email::MIME;
 use File::Temp qw/tempdir/;
-eval { require IPC::Run };
-plan skip_all => "missing IPC::Run for t/cgi.t" if $@;
-
-use constant CGI => "blib/script/public-inbox.cgi";
+require './t/common.perl';
 my $tmpdir = tempdir('pi-cgi-XXXXXX', TMPDIR => 1, CLEANUP => 1);
 my $home = "$tmpdir/pi-home";
 my $pi_home = "$home/.public-inbox";
@@ -145,11 +142,6 @@ EOF
 
 done_testing();
 
-sub run_with_env {
-	my ($env, @args) = @_;
-	IPC::Run::run(@args, init => sub { %ENV = (%ENV, %$env) });
-}
-
 sub cgi_run {
 	my %env = (
 		PATH_INFO => $_[0],
@@ -162,7 +154,9 @@ sub cgi_run {
 		HTTP_HOST => 'test.example.com',
 	);
 	my ($in, $out, $err) = ("", "", "");
-	my $rc = run_with_env(\%env, [CGI], \$in, \$out, \$err);
+	my $rdr = { 0 => \$in, 1 => \$out, 2 => \$err };
+	run_script(['.cgi'], \%env, $rdr);
+	die "unexpected error: \$?=$?" if $?;
 	my ($head, $body) = split(/\r\n\r\n/, $out, 2);
-	{ head => $head, body => $body, rc => $rc, err => $err }
+	{ head => $head, body => $body, err => $err }
 }
diff --git a/t/common.perl b/t/common.perl
index d4a0fcd2..c5693080 100644
--- a/t/common.perl
+++ b/t/common.perl
@@ -1,7 +1,7 @@
 # Copyright (C) 2015-2019 all contributors <meta@public-inbox.org>
 # License: AGPL-3.0+ <https://www.gnu.org/licenses/agpl-3.0.txt>
 
-use Fcntl qw(FD_CLOEXEC F_SETFD F_GETFD);
+use Fcntl qw(FD_CLOEXEC F_SETFD F_GETFD :seek);
 use POSIX qw(dup2);
 use strict;
 use warnings;
@@ -68,4 +68,127 @@ sub require_git ($;$) {
 	1;
 }
 
+my %cached_scripts;
+sub key2script ($) {
+	my ($key) = @_;
+	return $key if $key =~ m!\A/!;
+	# n.b. we may have scripts which don't start with "public-inbox" in
+	# the future:
+	$key =~ s/\A([-\.])/public-inbox$1/;
+	'blib/script/'.$key;
+}
+
+sub _prepare_redirects ($) {
+	my ($fhref) = @_;
+	my @x = ([ \*STDIN, '<&' ], [ \*STDOUT, '>&' ], [ \*STDERR, '>&' ]);
+	for (my $fd = 0; $fd <= $#x; $fd++) {
+		my $fh = $fhref->[$fd] or next;
+		my ($oldfh, $mode) = @{$x[$fd]};
+		open $oldfh, $mode, $fh or die "$$oldfh $mode redirect: $!";
+	}
+}
+
+# $opt->{run_mode} (or $ENV{TEST_RUN_MODE}) allows chosing between
+# three ways to spawn our own short-lived Perl scripts for testing:
+#
+# 0 - (fork|vfork) + execve, the most realistic but slowest
+# 1 - preloading and running in a forked subprocess (fast)
+# 2 - preloading and running in current process (slightly faster than 1)
+#
+# 2 is not compatible with scripts which use "exit" (which we'll try to
+# avoid in the future).
+# The default is 2.
+our $run_script_exit_code;
+sub RUN_SCRIPT_EXIT () { "RUN_SCRIPT_EXIT\n" };
+sub run_script_exit (;$) {
+	$run_script_exit_code = $_[0] // 0;
+	die RUN_SCRIPT_EXIT;
+}
+
+sub run_script ($;$$) {
+	my ($cmd, $env, $opt) = @_;
+	my ($key, @argv) = @$cmd;
+	my $run_mode = $ENV{TEST_RUN_MODE} // $opt->{run_mode} // 1;
+	my $sub = $run_mode == 0 ? undef : ($cached_scripts{$key} //= do {
+		my $f = key2script($key);
+		open my $fh, '<', $f or die "open $f: $!";
+		my $str = do { local $/; <$fh> };
+		my ($fc, $rest) = ($key =~ m/([a-z])([a-z0-9]+)\z/);
+		$fc = uc($fc);
+		my $pkg = "PublicInbox::TestScript::$fc$rest";
+		eval <<EOF;
+package $pkg;
+use strict;
+use subs qw(exit);
+
+*exit = *::run_script_exit;
+sub main {
+$str
+	0;
+}
+1;
+EOF
+		$pkg->can('main');
+	}); # do
+
+	my $fhref = [];
+	my $spawn_opt = {};
+	for my $fd (0..2) {
+		my $redir = $opt->{$fd};
+		next unless ref($redir);
+		open my $fh, '+>', undef or die "open: $!";
+		$fhref->[$fd] = $fh;
+		$spawn_opt->{$fd} = fileno($fh);
+		next if $fd > 0;
+		$fh->autoflush(1);
+		print $fh $$redir or die "print: $!";
+		seek($fh, 0, SEEK_SET) or die "seek: $!";
+	}
+	if ($run_mode == 0) {
+		# spawn an independent new process, like real-world use cases:
+		require PublicInbox::Spawn;
+		my $cmd = [ key2script($key), @argv ];
+		my $pid = PublicInbox::Spawn::spawn($cmd, $env, $spawn_opt);
+		defined($pid) or die "spawn: $!";
+		if (defined $pid) {
+			my $r = waitpid($pid, 0);
+			defined($r) or die "waitpid: $!";
+			$r == $pid or die "waitpid: expected $pid, got $r";
+		}
+	} else { # localize and run everything in the same process:
+		local *STDIN = *STDIN;
+		local *STDOUT = *STDOUT;
+		local *STDERR = *STDERR;
+		local %ENV = $env ? (%ENV, %$env) : %ENV;
+		local %SIG = %SIG;
+		_prepare_redirects($fhref);
+		local @ARGV = @argv;
+		$run_script_exit_code = undef;
+		my $exit_code = eval { $sub->(@argv) };
+		if ($@ eq RUN_SCRIPT_EXIT) {
+			$@ = '';
+			$exit_code = $run_script_exit_code;
+			$? = ($exit_code << 8);
+		} elsif (defined($exit_code)) {
+			$? = ($exit_code << 8);
+		} elsif ($@) { # mimic die() behavior when uncaught
+			warn "E: eval-ed $key: $@\n";
+			$? = ($! << 8) if $!;
+			$? = (255 << 8) if $? == 0;
+		} else {
+			die "BUG: eval-ed $key: no exit code or \$@\n";
+		}
+	}
+
+	# slurp the redirects back into user-supplied strings
+	for my $fd (1..2) {
+		my $fh = $fhref->[$fd] or next;
+		seek($fh, 0, SEEK_SET) or die "seek: $!";
+		my $redir = $opt->{$fd};
+		local $/;
+		$$redir = <$fh>;
+	}
+	$? == 0;
+}
+
 1;

^ permalink raw reply related	[relevance 6%]

* [PATCH 00/29] speed up tests by preloading
@ 2019-11-15  9:50  7% Eric Wong
  2019-11-15  9:50  6% ` [PATCH 12/29] t/common: introduce run_script wrapper for t/cgi.t Eric Wong
  0 siblings, 1 reply; 2+ results
From: Eric Wong @ 2019-11-15  9:50 UTC (permalink / raw)
  To: meta

On my fastest system, this brings "make check" time down from
~17s to ~10s.  This also improves consistency of our test suite,
adds ENV{TAIL} support to all daemons, and removes the test-time
dependency on the IPC::Run module.

Several cleanups were necessary to limit the scope of some
references and minor bugs were found (and fixed) in preparation
for this.  Most of the changes were to explicitly pass global
variables into subs to avoid warnings.

TEST_RUN_MODE=0 can be set in the environment to restore
real-world behavior with (v)fork && execve.

Eric Wong (29):
  edit: pass global variables into subs
  edit: use OO API of File::Temp to shorten lifetime
  admin: get rid of singleton $CFG var
  index: pass global variables into subs
  init: pass global variables into subs
  mda: pass global variables into subs
  learn: pass global variables into subs
  inboxwritable: add ->cleanup method
  import: only pass Inbox object to SearchIdx->new
  xapcmd: do not fire END and DESTROY handlers in child
  spawn: which: allow embedded slash for relative path
  t/common: introduce run_script wrapper for t/cgi.t
  t/edit: switch to use run_script
  t/init: convert to using run_script
  t/purge: convert to run_script
  t/v2mirror: get rid of IPC::Run dependency
  t/mda: switch to run_script for testing
  t/mda_filter_rubylang: drop IPC::Run dependency
  doc: remove IPC::Run as a dev and test dependency
  t/v2mirror: switch to default run_mode for speedup
  t/convert-compact: convert to run_script
  t/httpd: use run_script for -init
  t/watch_maildir_v2: use run_script for -init
  t/nntpd: use run_script for -init
  t/watch_filter_rubylang: run_script for -init and -index
  t/v2mda: switch to run_script in many places
  t/indexlevels-mirror*: switch to run_script
  t/xcpdb-reshard: use run_script for -xcpdb
  t/common: start_script replaces spawn_listener

 INSTALL                          |   4 -
 ci/deps.perl                     |   1 -
 lib/PublicInbox/Admin.pm         |   9 +-
 lib/PublicInbox/Import.pm        |   4 +-
 lib/PublicInbox/InboxWritable.pm |   4 +
 lib/PublicInbox/Spawn.pm         |   2 +-
 lib/PublicInbox/Xapcmd.pm        |   5 +-
 script/public-inbox-edit         |  40 ++---
 script/public-inbox-index        |   3 +-
 script/public-inbox-init         |  27 +++-
 script/public-inbox-learn        |   8 +-
 script/public-inbox-mda          |  12 +-
 t/cgi.t                          |  16 +-
 t/common.perl                    | 248 ++++++++++++++++++++++++++++---
 t/convert-compact.t              |  18 +--
 t/edit.t                         |  65 ++++----
 t/git-http-backend.t             |  14 +-
 t/httpd-corner.t                 |  48 +++---
 t/httpd-https.t                  |  28 +---
 t/httpd-unix.t                   |  47 +++---
 t/httpd.t                        |  18 +--
 t/indexlevels-mirror.t           |  24 +--
 t/init.t                         |  85 +++++------
 t/mda.t                          |  53 ++++---
 t/mda_filter_rubylang.t          |  17 +--
 t/nntpd-tls.t                    |  29 +---
 t/nntpd-validate.t               |  27 ++--
 t/nntpd.t                        |  22 ++-
 t/perf-nntpd.t                   |  22 ++-
 t/purge.t                        |  20 +--
 t/v2mda.t                        |  38 ++---
 t/v2mirror.t                     |  55 +++----
 t/v2writable.t                   |   8 +-
 t/watch_filter_rubylang.t        |   6 +-
 t/watch_maildir_v2.t             |   4 +-
 t/www_listing.t                  |   8 +-
 t/xcpdb-reshard.t                |   5 +-
 37 files changed, 584 insertions(+), 460 deletions(-)


^ permalink raw reply	[relevance 7%]

Results 1-2 of 2 | reverse | options above
-- pct% links below jump to the message on this page, permalinks otherwise --
2019-11-15  9:50  7% [PATCH 00/29] speed up tests by preloading Eric Wong
2019-11-15  9:50  6% ` [PATCH 12/29] t/common: introduce run_script wrapper for t/cgi.t Eric Wong

Code repositories for project(s) associated with this public inbox

	https://80x24.org/public-inbox.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).