1
0
mirror of https://github.com/git/git.git synced 2024-09-29 17:51:22 +02:00
git/perl/Git/Packet.pm

174 lines
4.1 KiB
Perl
Raw Normal View History

package Git::Packet;
use 5.008;
use strict;
perl: check for perl warnings while running tests We set "use warnings" in most of our perl code to catch problems. But as the name implies, warnings just emit a message to stderr and don't otherwise affect the program. So our tests are quite likely to miss that warnings are being spewed, as most of them do not look at stderr. We could ask perl to make all warnings fatal, but this is likely annoying for non-developers, who would rather have a running program with a warning than something that refuses to work at all. So instead, let's teach the perl code to respect an environment variable (GIT_PERL_FATAL_WARNINGS) to increase the severity of the warnings. This can be set for day-to-day running if people want to be really pedantic, but the primary use is to trigger it within the test suite. We could also trigger that for every test run, but likewise even the tests failing may be annoying to distro builders, etc (just as -Werror would be for compiling C code). So we'll tie it to a special test-mode variable (GIT_TEST_PERL_FATAL_WARNINGS) that can be set in the environment or as a Makefile knob, and we'll automatically turn the knob when DEVELOPER=1 is set. That should give developers and CI the more careful view without disrupting normal users or packagers. Note that the mapping from the GIT_TEST_* form to the GIT_* form in test-lib.sh is necessary even if they had the same name: the perl scripts need it to be normalized to a perl truth value, and we also have to make sure it's exported (we might have gotten it from the environment, but we might also have gotten it from GIT-BUILD-OPTIONS directly). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>
2020-10-22 05:24:00 +02:00
use warnings $ENV{GIT_PERL_FATAL_WARNINGS} ? qw(FATAL all) : ();
BEGIN {
require Exporter;
if ($] < 5.008003) {
*import = \&Exporter::import;
} else {
# Exporter 5.57 which supports this invocation was
# released with perl 5.8.3
Exporter->import('import');
}
}
our @EXPORT = qw(
packet_compare_lists
packet_bin_read
packet_txt_read
packet_key_val_read
packet_bin_write
packet_txt_write
packet_flush
packet_initialize
packet_read_capabilities
packet_read_and_check_capabilities
packet_check_and_write_capabilities
);
our @EXPORT_OK = @EXPORT;
sub packet_compare_lists {
my ($expect, @result) = @_;
my $ix;
if (scalar @$expect != scalar @result) {
return undef;
}
for ($ix = 0; $ix < $#result; $ix++) {
if ($expect->[$ix] ne $result[$ix]) {
return undef;
}
}
return 1;
}
sub packet_bin_read {
my $buffer;
my $bytes_read = read STDIN, $buffer, 4;
if ( $bytes_read == 0 ) {
# EOF - Git stopped talking to us!
return ( -1, "" );
} elsif ( $bytes_read != 4 ) {
die "invalid packet: '$buffer'";
}
my $pkt_size = hex($buffer);
if ( $pkt_size == 0 ) {
return ( 1, "" );
} elsif ( $pkt_size > 4 ) {
my $content_size = $pkt_size - 4;
$bytes_read = read STDIN, $buffer, $content_size;
if ( $bytes_read != $content_size ) {
die "invalid packet ($content_size bytes expected; $bytes_read bytes read)";
}
return ( 0, $buffer );
} else {
die "invalid packet size: $pkt_size";
}
}
sub remove_final_lf_or_die {
my $buf = shift;
if ( $buf =~ s/\n$// ) {
return $buf;
}
die "A non-binary line MUST be terminated by an LF.\n"
. "Received: '$buf'";
}
sub packet_txt_read {
my ( $res, $buf ) = packet_bin_read();
if ( $res != -1 and $buf ne '' ) {
$buf = remove_final_lf_or_die($buf);
}
return ( $res, $buf );
}
# Read a text packet, expecting that it is in the form "key=value" for
# the given $key. An EOF does not trigger any error and is reported
# back to the caller (like packet_txt_read() does). Die if the "key"
# part of "key=value" does not match the given $key, or the value part
# is empty.
sub packet_key_val_read {
my ( $key ) = @_;
my ( $res, $buf ) = packet_txt_read();
if ( $res == -1 or ( $buf =~ s/^$key=// and $buf ne '' ) ) {
return ( $res, $buf );
}
die "bad $key: '$buf'";
}
sub packet_bin_write {
my $buf = shift;
print STDOUT sprintf( "%04x", length($buf) + 4 );
print STDOUT $buf;
STDOUT->flush();
}
sub packet_txt_write {
packet_bin_write( $_[0] . "\n" );
}
sub packet_flush {
print STDOUT sprintf( "%04x", 0 );
STDOUT->flush();
}
sub packet_initialize {
my ($name, $version) = @_;
packet_compare_lists([0, $name . "-client"], packet_txt_read()) ||
die "bad initialize";
packet_compare_lists([0, "version=" . $version], packet_txt_read()) ||
die "bad version";
packet_compare_lists([1, ""], packet_bin_read()) ||
die "bad version end";
packet_txt_write( $name . "-server" );
packet_txt_write( "version=" . $version );
packet_flush();
}
sub packet_read_capabilities {
my @cap;
while (1) {
my ( $res, $buf ) = packet_bin_read();
if ( $res == -1 ) {
die "unexpected EOF when reading capabilities";
}
return ( $res, @cap ) if ( $res != 0 );
$buf = remove_final_lf_or_die($buf);
unless ( $buf =~ s/capability=// ) {
die "bad capability buf: '$buf'";
}
push @cap, $buf;
}
}
# Read remote capabilities and check them against capabilities we require
sub packet_read_and_check_capabilities {
my @required_caps = @_;
my ($res, @remote_caps) = packet_read_capabilities();
my %remote_caps = map { $_ => 1 } @remote_caps;
foreach (@required_caps) {
unless (exists($remote_caps{$_})) {
die "required '$_' capability not available from remote" ;
}
}
return %remote_caps;
}
# Check our capabilities we want to advertise against the remote ones
# and then advertise our capabilities
sub packet_check_and_write_capabilities {
my ($remote_caps, @our_caps) = @_;
foreach (@our_caps) {
unless (exists($remote_caps->{$_})) {
die "our capability '$_' is not available from remote"
}
packet_txt_write( "capability=" . $_ );
}
packet_flush();
}
1;