1#!/usr/bin/env perl 2#*************************************************************************** 3# _ _ ____ _ 4# Project ___| | | | _ \| | 5# / __| | | | |_) | | 6# | (__| |_| | _ <| |___ 7# \___|\___/|_| \_\_____| 8# 9# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al. 10# 11# This software is licensed as described in the file COPYING, which 12# you should have received as part of this distribution. The terms 13# are also available at https://curl.se/docs/copyright.html. 14# 15# You may opt to use, copy, modify, merge, publish, distribute and/or sell 16# copies of the Software, and permit persons to whom the Software is 17# furnished to do so, under the terms of the COPYING file. 18# 19# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 20# KIND, either express or implied. 21# 22# SPDX-License-Identifier: curl 23# 24#*************************************************************************** 25 26#======================================================================= 27# Read a test definition which exercises curl's --libcurl option. 28# Generate either compilable source code for a new test tool, 29# or a new test definition which runs the tool and expects the 30# same output. 31# This should verify that the --libcurl code really does perform 32# the same actions as the original curl invocation. 33#----------------------------------------------------------------------- 34# The output of curl's --libcurl option differs in several ways from 35# the code needed to integrate with the test tool environment: 36# - #include "test.h" 37# - no call of curl_global_init & curl_global_cleanup 38# - main() function vs. test() function 39# - no checking of curl_easy_setopt calls vs. test_setopt wrapper 40# - handling of stdout 41# - variable names ret & hnd vs. res & curl 42# - URL as literal string vs. passed as argument 43#======================================================================= 44use strict; 45require "getpart.pm"; 46 47# Boilerplate code for test tool 48my $head = 49'#include "test.h" 50#include "memdebug.h" 51 52int test(char *URL) 53{ 54 CURLcode res; 55 CURL *curl; 56'; 57# Other declarations from --libcurl come here 58# e.g. curl_slist 59my $init = 60' 61 if (curl_global_init(CURL_GLOBAL_ALL) != CURLE_OK) { 62 fprintf(stderr, "curl_global_init() failed\n"); 63 return TEST_ERR_MAJOR_BAD; 64 } 65 66 if ((curl = curl_easy_init()) == NULL) { 67 fprintf(stderr, "curl_easy_init() failed\n"); 68 curl_global_cleanup(); 69 return TEST_ERR_MAJOR_BAD; 70 } 71'; 72# Option setting, perform and cleanup come here 73my $exit = 74' curl_global_cleanup(); 75 76 return (int)res; 77} 78'; 79 80my $myname = leaf($0); 81sub usage {die "Usage: $myname -c|-test=num testfile\n";} 82 83sub main { 84 @ARGV == 2 85 or usage; 86 my($opt,$testfile) = @ARGV; 87 88 if(loadtest($testfile)) { 89 die "$myname: $testfile doesn't look like a test case\n"; 90 } 91 92 my $comment = sprintf("DO NOT EDIT - generated from %s by %s", 93 leaf($testfile), $myname); 94 if($opt eq '-c') { 95 generate_c($comment); 96 } 97 elsif(my($num) = $opt =~ /^-test=(\d+)$/) { 98 generate_test($comment, $num); 99 } 100 else { 101 usage; 102 } 103} 104 105sub generate_c { 106 my($comment) = @_; 107 # Fetch the generated code, which is the output file checked by 108 # the old test. 109 my @libcurl = getpart("verify", "file") 110 or die "$myname: no <verify><file> section found\n"; 111 112 # Mangle the code into a suitable form for a test tool. 113 # We want to extract the important parts (declarations, 114 # URL, setopt calls, cleanup code) from the --libcurl 115 # boilerplate and insert them into a new boilerplate. 116 my(@decl,@code); 117 # First URL passed in as argument, others as global 118 my @urlvars = ('URL', 'libtest_arg2', 'libtest_arg3'); 119 my($seen_main,$seen_setopt,$seen_return); 120 foreach (@libcurl) { 121 # Check state changes first (even though it 122 # duplicates some matches) so that the other tests 123 # are in a logical order). 124 if(/^int main/) { 125 $seen_main = 1; 126 } 127 if($seen_main and /curl_easy_setopt/) { 128 # Don't match 'curl_easy_setopt' in comment! 129 $seen_setopt = 1; 130 } 131 if(/^\s*return/) { 132 $seen_return = 1; 133 } 134 135 # Now filter the code according to purpose 136 if(! $seen_main) { 137 next; 138 } 139 elsif(! $seen_setopt) { 140 if(/^\s*(int main|\{|CURLcode |CURL |hnd = curl_easy_init)/) { 141 # Initialization handled by boilerplate 142 next; 143 } 144 else { 145 push @decl, $_; 146 } 147 } 148 elsif(! $seen_return) { 149 if(/CURLOPT_URL/) { 150 # URL is passed in as argument or by global 151 my $var = shift @urlvars; 152 s/\"[^\"]*\"/$var/; 153 } 154 s/\bhnd\b/curl/; 155 # Convert to macro wrapper 156 s/curl_easy_setopt/test_setopt/; 157 if(/curl_easy_perform/) { 158 s/\bret\b/res/; 159 push @code, $_; 160 push @code, "test_cleanup:\n"; 161 } 162 else { 163 push @code, $_; 164 } 165 } 166 } 167 168 print ("/* $comment */\n", 169 $head, 170 @decl, 171 $init, 172 @code, 173 $exit); 174} 175 176# Read the original test data file and transform it 177# - add a "DO NOT EDIT comment" 178# - replace CURLOPT_URL string with URL variable 179# - remove <verify><file> section (was the --libcurl output) 180# - insert a <client><tool> section with our new C program name 181# - replace <client><command> section with the URL 182sub generate_test { 183 my($comment,$newnumber) = @_; 184 my @libcurl = getpart("verify", "file") 185 or die "$myname: no <verify><file> section found\n"; 186 # Scan the --libcurl code to find the URL used. 187 my $url; 188 foreach (@libcurl) { 189 if(my($u) = /CURLOPT_URL, \"([^\"]*)\"/) { 190 $url = $u; 191 } 192 } 193 die "$myname: CURLOPT_URL not found\n" 194 unless defined $url; 195 196 # Traverse the pseudo-XML transforming as required 197 my @new; 198 my(@path,$path,$skip); 199 foreach (getall()) { 200 if(my($end) = /\s*<(\/?)testcase>/) { 201 push @new, $_; 202 push @new, "# $comment\n" 203 unless $end; 204 } 205 elsif(my($tag) = /^\s*<(\w+)/) { 206 push @path, $tag; 207 $path = join '/', @path; 208 if($path eq 'verify/file') { 209 $skip = 1; 210 } 211 push @new, $_ 212 unless $skip; 213 if($path eq 'client') { 214 push @new, ("<tool>\n", 215 "lib$newnumber\n", 216 "</tool>\n"); 217 } 218 elsif($path eq 'client/command') { 219 push @new, sh_quote($url)."\n"; 220 } 221 } 222 elsif(my($etag) = /^\s*<\/(\w+)/) { 223 my $tag = pop @path; 224 die "$myname: mismatched </$etag>\n" 225 unless $tag eq $etag; 226 push @new, $_ 227 unless $skip; 228 $skip -- 229 if $path eq 'verify/file'; 230 $path = join '/', @path; 231 } 232 else { 233 if($path eq 'client/command') { 234 # Replaced above 235 } 236 else { 237 push @new, $_ 238 unless $skip; 239 } 240 } 241 } 242 print @new; 243} 244 245sub leaf { 246 # Works for POSIX filenames 247 (my $path = shift) =~ s!.*/!!; 248 return $path; 249} 250 251sub sh_quote { 252 my $word = shift; 253 $word =~ s/[\$\"\'\\]/\\$&/g; 254 return '"' . $word . '"'; 255} 256 257main; 258