Merge branch 'jn/mergetool-hideresolved-is-optional'
[git] / t / t0021 / rot13-filter.pl
1 #
2 # Example implementation for the Git filter protocol version 2
3 # See Documentation/gitattributes.txt, section "Filter Protocol"
4 #
5 # Usage: rot13-filter.pl [--always-delay] <log path> <capabilities>
6 #
7 # Log path defines a debug log file that the script writes to. The
8 # subsequent arguments define a list of supported protocol capabilities
9 # ("clean", "smudge", etc).
10 #
11 # When --always-delay is given all pathnames with the "can-delay" flag
12 # that don't appear on the list bellow are delayed with a count of 1
13 # (see more below).
14 #
15 # This implementation supports special test cases:
16 # (1) If data with the pathname "clean-write-fail.r" is processed with
17 #     a "clean" operation then the write operation will die.
18 # (2) If data with the pathname "smudge-write-fail.r" is processed with
19 #     a "smudge" operation then the write operation will die.
20 # (3) If data with the pathname "error.r" is processed with any
21 #     operation then the filter signals that it cannot or does not want
22 #     to process the file.
23 # (4) If data with the pathname "abort.r" is processed with any
24 #     operation then the filter signals that it cannot or does not want
25 #     to process the file and any file after that is processed with the
26 #     same command.
27 # (5) If data with a pathname that is a key in the DELAY hash is
28 #     requested (e.g. "test-delay10.a") then the filter responds with
29 #     a "delay" status and sets the "requested" field in the DELAY hash.
30 #     The filter will signal the availability of this object after
31 #     "count" (field in DELAY hash) "list_available_blobs" commands.
32 # (6) If data with the pathname "missing-delay.a" is processed that the
33 #     filter will drop the path from the "list_available_blobs" response.
34 # (7) If data with the pathname "invalid-delay.a" is processed that the
35 #     filter will add the path "unfiltered" which was not delayed before
36 #     to the "list_available_blobs" response.
37 #
38
39 use 5.008;
40 sub gitperllib {
41         # Git assumes that all path lists are Unix-y colon-separated ones. But
42         # when the Git for Windows executes the test suite, its MSYS2 Bash
43         # calls git.exe, and colon-separated path lists are converted into
44         # Windows-y semicolon-separated lists of *Windows* paths (which
45         # naturally contain a colon after the drive letter, so splitting by
46         # colons simply does not cut it).
47         #
48         # Detect semicolon-separated path list and handle them appropriately.
49
50         if ($ENV{GITPERLLIB} =~ /;/) {
51                 return split(/;/, $ENV{GITPERLLIB});
52         }
53         return split(/:/, $ENV{GITPERLLIB});
54 }
55 use lib (gitperllib());
56 use strict;
57 use warnings;
58 use IO::File;
59 use Git::Packet;
60
61 my $MAX_PACKET_CONTENT_SIZE = 65516;
62
63 my $always_delay = 0;
64 if ( $ARGV[0] eq '--always-delay' ) {
65         $always_delay = 1;
66         shift @ARGV;
67 }
68
69 my $log_file                = shift @ARGV;
70 my @capabilities            = @ARGV;
71
72 open my $debug, ">>", $log_file or die "cannot open log file: $!";
73
74 my %DELAY = (
75         'test-delay10.a' => { "requested" => 0, "count" => 1 },
76         'test-delay11.a' => { "requested" => 0, "count" => 1 },
77         'test-delay20.a' => { "requested" => 0, "count" => 2 },
78         'test-delay10.b' => { "requested" => 0, "count" => 1 },
79         'missing-delay.a' => { "requested" => 0, "count" => 1 },
80         'invalid-delay.a' => { "requested" => 0, "count" => 1 },
81 );
82
83 sub rot13 {
84         my $str = shift;
85         $str =~ y/A-Za-z/N-ZA-Mn-za-m/;
86         return $str;
87 }
88
89 print $debug "START\n";
90 $debug->flush();
91
92 packet_initialize("git-filter", 2);
93
94 my %remote_caps = packet_read_and_check_capabilities("clean", "smudge", "delay");
95 packet_check_and_write_capabilities(\%remote_caps, @capabilities);
96
97 print $debug "init handshake complete\n";
98 $debug->flush();
99
100 while (1) {
101         my ( $res, $command ) = packet_key_val_read("command");
102         if ( $res == -1 ) {
103                 print $debug "STOP\n";
104                 exit();
105         }
106         print $debug "IN: $command";
107         $debug->flush();
108
109         if ( $command eq "list_available_blobs" ) {
110                 # Flush
111                 packet_compare_lists([1, ""], packet_bin_read()) ||
112                         die "bad list_available_blobs end";
113
114                 foreach my $pathname ( sort keys %DELAY ) {
115                         if ( $DELAY{$pathname}{"requested"} >= 1 ) {
116                                 $DELAY{$pathname}{"count"} = $DELAY{$pathname}{"count"} - 1;
117                                 if ( $pathname eq "invalid-delay.a" ) {
118                                         # Send Git a pathname that was not delayed earlier
119                                         packet_txt_write("pathname=unfiltered");
120                                 }
121                                 if ( $pathname eq "missing-delay.a" ) {
122                                         # Do not signal Git that this file is available
123                                 } elsif ( $DELAY{$pathname}{"count"} == 0 ) {
124                                         print $debug " $pathname";
125                                         packet_txt_write("pathname=$pathname");
126                                 }
127                         }
128                 }
129
130                 packet_flush();
131
132                 print $debug " [OK]\n";
133                 $debug->flush();
134                 packet_txt_write("status=success");
135                 packet_flush();
136         } else {
137                 my ( $res, $pathname ) = packet_key_val_read("pathname");
138                 if ( $res == -1 ) {
139                         die "unexpected EOF while expecting pathname";
140                 }
141                 print $debug " $pathname";
142                 $debug->flush();
143
144                 # Read until flush
145                 my ( $done, $buffer ) = packet_txt_read();
146                 while ( $buffer ne '' ) {
147                         if ( $buffer eq "can-delay=1" ) {
148                                 if ( exists $DELAY{$pathname} and $DELAY{$pathname}{"requested"} == 0 ) {
149                                         $DELAY{$pathname}{"requested"} = 1;
150                                 } elsif ( !exists $DELAY{$pathname} and $always_delay ) {
151                                         $DELAY{$pathname} = { "requested" => 1, "count" => 1 };
152                                 }
153                         } elsif ($buffer =~ /^(ref|treeish|blob)=/) {
154                                 print $debug " $buffer";
155                         } else {
156                                 # In general, filters need to be graceful about
157                                 # new metadata, since it's documented that we
158                                 # can pass any key-value pairs, but for tests,
159                                 # let's be a little stricter.
160                                 die "Unknown message '$buffer'";
161                         }
162
163                         ( $done, $buffer ) = packet_txt_read();
164                 }
165                 if ( $done == -1 ) {
166                         die "unexpected EOF after pathname '$pathname'";
167                 }
168
169                 my $input = "";
170                 {
171                         binmode(STDIN);
172                         my $buffer;
173                         my $done = 0;
174                         while ( !$done ) {
175                                 ( $done, $buffer ) = packet_bin_read();
176                                 $input .= $buffer;
177                         }
178                         if ( $done == -1 ) {
179                                 die "unexpected EOF while reading input for '$pathname'";
180                         }                       
181                         print $debug " " . length($input) . " [OK] -- ";
182                         $debug->flush();
183                 }
184
185                 my $output;
186                 if ( exists $DELAY{$pathname} and exists $DELAY{$pathname}{"output"} ) {
187                         $output = $DELAY{$pathname}{"output"}
188                 } elsif ( $pathname eq "error.r" or $pathname eq "abort.r" ) {
189                         $output = "";
190                 } elsif ( $command eq "clean" and grep( /^clean$/, @capabilities ) ) {
191                         $output = rot13($input);
192                 } elsif ( $command eq "smudge" and grep( /^smudge$/, @capabilities ) ) {
193                         $output = rot13($input);
194                 } else {
195                         die "bad command '$command'";
196                 }
197
198                 if ( $pathname eq "error.r" ) {
199                         print $debug "[ERROR]\n";
200                         $debug->flush();
201                         packet_txt_write("status=error");
202                         packet_flush();
203                 } elsif ( $pathname eq "abort.r" ) {
204                         print $debug "[ABORT]\n";
205                         $debug->flush();
206                         packet_txt_write("status=abort");
207                         packet_flush();
208                 } elsif ( $command eq "smudge" and
209                         exists $DELAY{$pathname} and
210                         $DELAY{$pathname}{"requested"} == 1 ) {
211                         print $debug "[DELAYED]\n";
212                         $debug->flush();
213                         packet_txt_write("status=delayed");
214                         packet_flush();
215                         $DELAY{$pathname}{"requested"} = 2;
216                         $DELAY{$pathname}{"output"} = $output;
217                 } else {
218                         packet_txt_write("status=success");
219                         packet_flush();
220
221                         if ( $pathname eq "${command}-write-fail.r" ) {
222                                 print $debug "[WRITE FAIL]\n";
223                                 $debug->flush();
224                                 die "${command} write error";
225                         }
226
227                         print $debug "OUT: " . length($output) . " ";
228                         $debug->flush();
229
230                         while ( length($output) > 0 ) {
231                                 my $packet = substr( $output, 0, $MAX_PACKET_CONTENT_SIZE );
232                                 packet_bin_write($packet);
233                                 # dots represent the number of packets
234                                 print $debug ".";
235                                 if ( length($output) > $MAX_PACKET_CONTENT_SIZE ) {
236                                         $output = substr( $output, $MAX_PACKET_CONTENT_SIZE );
237                                 } else {
238                                         $output = "";
239                                 }
240                         }
241                         packet_flush();
242                         print $debug " [OK]\n";
243                         $debug->flush();
244                         packet_flush();
245                 }
246         }
247 }