1#include"test-tool.h" 2#include"cache.h" 3#include"argv-array.h" 4#include"run-command.h" 5#include"exec-cmd.h" 6#include"config.h" 7 8typedefint(fn_unit_test)(int argc,const char**argv); 9 10struct unit_test { 11 fn_unit_test *ut_fn; 12const char*ut_name; 13const char*ut_usage; 14}; 15 16#define MyOk 0 17#define MyError 1 18 19static intget_i(int*p_value,const char*data) 20{ 21char*endptr; 22 23if(!data || !*data) 24return MyError; 25 26*p_value =strtol(data, &endptr,10); 27if(*endptr || errno == ERANGE) 28return MyError; 29 30return MyOk; 31} 32 33/* 34 * Cause process to exit with the requested value via "return". 35 * 36 * Rely on test-tool.c:cmd_main() to call trace2_cmd_exit() 37 * with our result. 38 * 39 * Test harness can confirm: 40 * [] the process-exit value. 41 * [] the "code" field in the "exit" trace2 event. 42 * [] the "code" field in the "atexit" trace2 event. 43 * [] the "name" field in the "cmd_name" trace2 event. 44 * [] "def_param" events for all of the "interesting" pre-defined 45 * config settings. 46 */ 47static intut_001return(int argc,const char**argv) 48{ 49int rc; 50 51if(get_i(&rc, argv[0])) 52die("expect <exit_code>"); 53 54return rc; 55} 56 57/* 58 * Cause the process to exit with the requested value via "exit()". 59 * 60 * Test harness can confirm: 61 * [] the "code" field in the "exit" trace2 event. 62 * [] the "code" field in the "atexit" trace2 event. 63 * [] the "name" field in the "cmd_name" trace2 event. 64 * [] "def_param" events for all of the "interesting" pre-defined 65 * config settings. 66 */ 67static intut_002exit(int argc,const char**argv) 68{ 69int rc; 70 71if(get_i(&rc, argv[0])) 72die("expect <exit_code>"); 73 74exit(rc); 75} 76 77/* 78 * Send an "error" event with each value in argv. Normally, git only issues 79 * a single "error" event immediately before issuing an "exit" event (such 80 * as in die() or BUG()), but multiple "error" events are allowed. 81 * 82 * Test harness can confirm: 83 * [] a trace2 "error" event for each value in argv. 84 * [] the "name" field in the "cmd_name" trace2 event. 85 * [] (optional) the file:line in the "exit" event refers to this function. 86 */ 87static intut_003error(int argc,const char**argv) 88{ 89int k; 90 91if(!argv[0] || !*argv[0]) 92die("expect <error_message>"); 93 94for(k =0; k < argc; k++) 95error("%s", argv[k]); 96 97return0; 98} 99 100/* 101 * Run a child process and wait for it to finish and exit with its return code. 102 * test-tool trace2 004child [<child-command-line>] 103 * 104 * For example: 105 * test-tool trace2 004child git version 106 * test-tool trace2 004child test-tool trace2 001return 0 107 * test-tool trace2 004child test-tool trace2 004child test-tool trace2 004child 108 * test-tool trace2 004child git -c alias.xyz=version xyz 109 * 110 * Test harness can confirm: 111 * [] the "name" field in the "cmd_name" trace2 event. 112 * [] that the outer process has a single component SID (or depth "d0" in 113 * the PERF stream). 114 * [] that "child_start" and "child_exit" events are generated for the child. 115 * [] if the child process is an instrumented executable: 116 * [] that "version", "start", ..., "exit", and "atexit" events are 117 * generated by the child process. 118 * [] that the child process events have a multiple component SID (or 119 * depth "dN+1" in the PERF stream). 120 * [] that the child exit code is propagated to the parent process "exit" 121 * and "atexit" events.. 122 * [] (optional) that the "t_abs" field in the child process "atexit" event 123 * is less than the "t_rel" field in the "child_exit" event of the parent 124 * process. 125 * [] if the child process is like the alias example above, 126 * [] (optional) the child process attempts to run "git-xyx" as a dashed 127 * command. 128 * [] the child process emits an "alias" event with "xyz" => "version" 129 * [] the child process runs "git version" as a child process. 130 * [] the child process has a 3 component SID (or depth "d2" in the PERF 131 * stream). 132 */ 133static intut_004child(int argc,const char**argv) 134{ 135int result; 136 137/* 138 * Allow empty <child_command_line> so we can do arbitrarily deep 139 * command nesting and let the last one be null. 140 */ 141if(!argc) 142return0; 143 144 result =run_command_v_opt(argv,0); 145exit(result); 146} 147 148/* 149 * Exec a git command. This may either create a child process (Windows) 150 * or replace the existing process. 151 * test-tool trace2 005exec <git_command_args> 152 * 153 * For example: 154 * test-tool trace2 005exec version 155 * 156 * Test harness can confirm (on Windows): 157 * [] the "name" field in the "cmd_name" trace2 event. 158 * [] that the outer process has a single component SID (or depth "d0" in 159 * the PERF stream). 160 * [] that "exec" and "exec_result" events are generated for the child 161 * process (since the Windows compatibility layer fakes an exec() with 162 * a CreateProcess(), WaitForSingleObject(), and exit()). 163 * [] that the child process has multiple component SID (or depth "dN+1" 164 * in the PERF stream). 165 * 166 * Test harness can confirm (on platforms with a real exec() function): 167 * [] TODO talk about process replacement and how it affects SID. 168 */ 169static intut_005exec(int argc,const char**argv) 170{ 171int result; 172 173if(!argc) 174return0; 175 176 result =execv_git_cmd(argv); 177return result; 178} 179 180static intut_006data(int argc,const char**argv) 181{ 182const char*usage_error = 183"expect <cat0> <k0> <v0> [<cat1> <k1> <v1> [...]]"; 184 185if(argc %3!=0) 186die("%s", usage_error); 187 188while(argc) { 189if(!argv[0] || !*argv[0] || !argv[1] || !*argv[1] || 190!argv[2] || !*argv[2]) 191die("%s", usage_error); 192 193trace2_data_string(argv[0], the_repository, argv[1], argv[2]); 194 argv +=3; 195 argc -=3; 196} 197 198return0; 199} 200 201/* 202 * Usage: 203 * test-tool trace2 <ut_name_1> <ut_usage_1> 204 * test-tool trace2 <ut_name_2> <ut_usage_2> 205 * ... 206 */ 207#define USAGE_PREFIX"test-tool trace2" 208 209/* clang-format off */ 210static struct unit_test ut_table[] = { 211{ ut_001return,"001return","<exit_code>"}, 212{ ut_002exit,"002exit","<exit_code>"}, 213{ ut_003error,"003error","<error_message>+"}, 214{ ut_004child,"004child","[<child_command_line>]"}, 215{ ut_005exec,"005exec","<git_command_args>"}, 216{ ut_006data,"006data","[<category> <key> <value>]+"}, 217}; 218/* clang-format on */ 219 220/* clang-format off */ 221#define for_each_ut(k, ut_k) \ 222 for (k = 0, ut_k = &ut_table[k]; \ 223 k < ARRAY_SIZE(ut_table); \ 224 k++, ut_k = &ut_table[k]) 225/* clang-format on */ 226 227static intprint_usage(void) 228{ 229int k; 230struct unit_test *ut_k; 231 232fprintf(stderr,"usage:\n"); 233for_each_ut(k, ut_k) 234fprintf(stderr,"\t%s %s %s\n", USAGE_PREFIX, ut_k->ut_name, 235 ut_k->ut_usage); 236 237return129; 238} 239 240/* 241 * Issue various trace2 events for testing. 242 * 243 * We assume that these trace2 routines has already been called: 244 * [] trace2_initialize() [common-main.c:main()] 245 * [] trace2_cmd_start() [common-main.c:main()] 246 * [] trace2_cmd_name() [test-tool.c:cmd_main()] 247 * [] tracd2_cmd_list_config() [test-tool.c:cmd_main()] 248 * So that: 249 * [] the various trace2 streams are open. 250 * [] the process SID has been created. 251 * [] the "version" event has been generated. 252 * [] the "start" event has been generated. 253 * [] the "cmd_name" event has been generated. 254 * [] this writes various "def_param" events for interesting config values. 255 * 256 * We further assume that if we return (rather than exit()), trace2_cmd_exit() 257 * will be called by test-tool.c:cmd_main(). 258 */ 259intcmd__trace2(int argc,const char**argv) 260{ 261int k; 262struct unit_test *ut_k; 263 264 argc--;/* skip over "trace2" arg */ 265 argv++; 266 267if(argc) 268for_each_ut(k, ut_k) 269if(!strcmp(argv[0], ut_k->ut_name)) 270return ut_k->ut_fn(argc -1, argv +1); 271 272returnprint_usage(); 273}