#!/usr/bin/env perl #*************************************************************************** # _ _ ____ _ # Project ___| | | | _ \| | # / __| | | | |_) | | # | (__| |_| | _ <| |___ # \___|\___/|_| \_\_____| # # Copyright (C) 1998 - 2022, Daniel Stenberg, , et al. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at https://curl.se/docs/copyright.html. # # You may opt to use, copy, modify, merge, publish, distribute and/or sell # copies of the Software, and permit persons to whom the Software is # furnished to do so, under the terms of the COPYING file. # # This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY # KIND, either express or implied. # # SPDX-License-Identifier: curl # #*************************************************************************** #======================================================================= # Read a test definition which exercises curl's --libcurl option. # Generate either compilable source code for a new test tool, # or a new test definition which runs the tool and expects the # same output. # This should verify that the --libcurl code really does perform # the same actions as the original curl invocation. #----------------------------------------------------------------------- # The output of curl's --libcurl option differs in several ways from # the code needed to integrate with the test tool environment: # - #include "test.h" # - no call of curl_global_init & curl_global_cleanup # - main() function vs. test() function # - no checking of curl_easy_setopt calls vs. test_setopt wrapper # - handling of stdout # - variable names ret & hnd vs. res & curl # - URL as literal string vs. passed as argument #======================================================================= use strict; require "getpart.pm"; # Boilerplate code for test tool my $head = '#include "test.h" #include "memdebug.h" int test(char *URL) { CURLcode res; CURL *curl; '; # Other declarations from --libcurl come here # e.g. curl_slist my $init = ' if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { fprintf(stderr, "curl_global_init() failed\n"); return TEST_ERR_MAJOR_BAD; } if ((curl = curl_easy_init()) == NULL) { fprintf(stderr, "curl_easy_init() failed\n"); curl_global_cleanup(); return TEST_ERR_MAJOR_BAD; } '; # Option setting, perform and cleanup come here my $exit = ' curl_global_cleanup(); return (int)res; } '; my $myname = leaf($0); sub usage {die "Usage: $myname -c|-test=num testfile\n";} sub main { @ARGV == 2 or usage; my($opt,$testfile) = @ARGV; if(loadtest($testfile)) { die "$myname: $testfile doesn't look like a test case\n"; } my $comment = sprintf("DO NOT EDIT - generated from %s by %s", leaf($testfile), $myname); if($opt eq '-c') { generate_c($comment); } elsif(my($num) = $opt =~ /^-test=(\d+)$/) { generate_test($comment, $num); } else { usage; } } sub generate_c { my($comment) = @_; # Fetch the generated code, which is the output file checked by # the old test. my @libcurl = getpart("verify", "file") or die "$myname: no section found\n"; # Mangle the code into a suitable form for a test tool. # We want to extract the important parts (declarations, # URL, setopt calls, cleanup code) from the --libcurl # boilerplate and insert them into a new boilerplate. my(@decl,@code); # First URL passed in as argument, others as global my @urlvars = ('URL', 'libtest_arg2', 'libtest_arg3'); my($seen_main,$seen_setopt,$seen_return); foreach (@libcurl) { # Check state changes first (even though it # duplicates some matches) so that the other tests # are in a logical order). if(/^int main/) { $seen_main = 1; } if($seen_main and /curl_easy_setopt/) { # Don't match 'curl_easy_setopt' in comment! $seen_setopt = 1; } if(/^\s*return/) { $seen_return = 1; } # Now filter the code according to purpose if(! $seen_main) { next; } elsif(! $seen_setopt) { if(/^\s*(int main|\{|CURLcode |CURL |hnd = curl_easy_init)/) { # Initialization handled by boilerplate next; } else { push @decl, $_; } } elsif(! $seen_return) { if(/CURLOPT_URL/) { # URL is passed in as argument or by global my $var = shift @urlvars; s/\"[^\"]*\"/$var/; } s/\bhnd\b/curl/; # Convert to macro wrapper s/curl_easy_setopt/test_setopt/; if(/curl_easy_perform/) { s/\bret\b/res/; push @code, $_; push @code, "test_cleanup:\n"; } else { push @code, $_; } } } print ("/* $comment */\n", $head, @decl, $init, @code, $exit); } # Read the original test data file and transform it # - add a "DO NOT EDIT comment" # - replace CURLOPT_URL string with URL variable # - remove section (was the --libcurl output) # - insert a section with our new C program name # - replace section with the URL sub generate_test { my($comment,$newnumber) = @_; my @libcurl = getpart("verify", "file") or die "$myname: no section found\n"; # Scan the --libcurl code to find the URL used. my $url; foreach (@libcurl) { if(my($u) = /CURLOPT_URL, \"([^\"]*)\"/) { $url = $u; } } die "$myname: CURLOPT_URL not found\n" unless defined $url; # Traverse the pseudo-XML transforming as required my @new; my(@path,$path,$skip); foreach (getall()) { if(my($end) = /\s*<(\/?)testcase>/) { push @new, $_; push @new, "# $comment\n" unless $end; } elsif(my($tag) = /^\s*<(\w+)/) { push @path, $tag; $path = join '/', @path; if($path eq 'verify/file') { $skip = 1; } push @new, $_ unless $skip; if($path eq 'client') { push @new, ("\n", "lib$newnumber\n", "\n"); } elsif($path eq 'client/command') { push @new, sh_quote($url)."\n"; } } elsif(my($etag) = /^\s*<\/(\w+)/) { my $tag = pop @path; die "$myname: mismatched \n" unless $tag eq $etag; push @new, $_ unless $skip; $skip -- if $path eq 'verify/file'; $path = join '/', @path; } else { if($path eq 'client/command') { # Replaced above } else { push @new, $_ unless $skip; } } } print @new; } sub leaf { # Works for POSIX filenames (my $path = shift) =~ s!.*/!!; return $path; } sub sh_quote { my $word = shift; $word =~ s/[\$\"\'\\]/\\$&/g; return '"' . $word . '"'; } main;