123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537openPpxlibopenBackend.Compiler_modulesopenCore_kernelopenExpect_test_common.StdopenExpect_test_matcher.StdopenMlt_parsermoduleClflags=Ocaml_common.ClflagsmoduleCompmisc=Ocaml_common.CompmiscmodulePrintast=Ocaml_common.PrintastmoduleWarnings=Ocaml_common.Warningsletparse_contents~fnamecontents=letlexbuf=Lexing.from_stringcontentsinlexbuf.lex_curr_p<-{pos_fname=fname;pos_lnum=1;pos_bol=0;pos_cnum=0};Ocaml_common.Location.input_name:=fname;Parse.use_filelexbuf;;letreset_line_numbers=reffalseletline_numbers_delta=ref0let()=Caml.Hashtbl.addToploop.directive_table"reset_line_numbers"(Directive_none(fun()->reset_line_numbers:=true));;letprint_line_numbers=reffalselet()=Caml.Hashtbl.addToploop.directive_table"print_line_numbers"(Directive_bool(funx->print_line_numbers:=x));;letprint_line_numberppfline=if!print_line_numbersthenFormat.fprintfppf"%d"lineelseFormat.pp_print_stringppf"_";;[%%ifocaml_version<(4,08,0)]letprint_locppf(loc:Location.t)=letline=loc.loc_start.pos_lnuminletstartchar=loc.loc_start.pos_cnum-loc.loc_start.pos_bolinletendchar=loc.loc_end.pos_cnum-loc.loc_start.pos_cnum+startcharinFormat.fprintfppf"Line %a"print_line_numberline;ifstartchar>=0thenFormat.fprintfppf", characters %d-%d"startcharendchar;Format.fprintfppf":@.";;;letrecerror_reporterppf({loc;msg;sub;if_highlight=_}:Ocaml_common.Location.error)=print_locppfloc;Format.fprintfppf"Error: %s"msg;List.itersub~f:(funerr->Format.fprintfppf"@\n@[<2>%a@]"error_reportererr);;[%%endif][%%ifocaml_version<(4,06,0)]letwarning_printerlocppfw=ifWarnings.is_activewthenbeginprint_locppfloc;Format.fprintfppf"Warning %a@."Warnings.printwend[%%elifocaml_version<(4,08,0)]letwarning_printerlocppfw=matchWarnings.reportwwith|`Inactive->()|`Active{Warnings.number;message;is_error;sub_locs=_}->print_locppfloc;ifis_errorthenFormat.fprintfppf"Error (Warning %d): %s@."numbermessageelseFormat.fprintfppf"Warning %d: %s@."numbermessage[%%elifocaml_version>=(4,08,0)]letwarning_reporter=Ocaml_common.Location.default_warning_reporterletalert_reporter=Ocaml_common.Location.default_alert_reporter[%%endif];;[%%ifocaml_version>=(4,08,0)]letreport_printer()=letprinter=Ocaml_common.Location.default_report_printer()inletprint_loc__reportppfloc=letline=loc.loc_start.pos_lnuminletstartchar=loc.loc_start.pos_cnum-loc.loc_start.pos_bolinletendchar=loc.loc_end.pos_cnum-loc.loc_start.pos_cnum+startcharinFormat.fprintfppf"Line %a"print_line_numberline;ifstartchar>=0thenFormat.fprintfppf", characters %d-%d"startcharendchar;Format.fprintfppf":@."in{printerwithOcaml_common.Location.pp_main_loc=print_loc;pp_submsg_loc=print_loc}[%%endif]typevar_and_value=V:'aref*'a->var_and_valueletprotect_vars=letset_varsl=List.iterl~f:(fun(V(r,v))->r:=v)infunvars~f->letbackup=List.mapvars~f:(fun(V(r,_))->V(r,!r))inset_varsvars;protect~finally:(fun()->set_varsbackup)~f;;[%%ifocaml_version<(4,08,0)]letcapture_compiler_stuffppf~f=protect_vars[V(Ocaml_common.Location.formatter_for_warnings,ppf);V(Ocaml_common.Location.warning_printer,warning_printer);V(Ocaml_common.Location.error_reporter,error_reporter)]~f[%%else]letcapture_compiler_stuffppf~f=protect_vars[V(Ocaml_common.Location.formatter_for_warnings,ppf);V(Ocaml_common.Location.warning_reporter,warning_reporter);V(Ocaml_common.Location.report_printer,report_printer);V(Ocaml_common.Location.alert_reporter,alert_reporter)]~f[%%endif];;letapply_rewriters=function|Ptop_dir_asx->x|Ptop_defs->Ptop_def(Driver.map_structures|>Migrate_parsetree.Driver.migrate_some_structure(modulePpxlib_ast.Selected_ast));;letverbose=reffalselet()=Caml.Hashtbl.addToploop.directive_table"verbose"(Directive_bool(funx->verbose:=x));;letshift_line_numbers=objectinherit[int]Ast_traverse.map_with_contextmethod!positiondeltapos={poswithpos_lnum=pos.pos_lnum+delta}endletexec_phraseppfphrase=if!reset_line_numbersthenbeginmatchphrasewith|Ptop_def(st::_)->reset_line_numbers:=false;line_numbers_delta:=1-st.pstr_loc.loc_start.pos_lnum|_->()end;letphrase=match!line_numbers_deltawith|0->phrase|n->shift_line_numbers#toplevel_phrasenphraseinletphrase=apply_rewritersphraseinletmoduleJs=Ppxlib_ast.Selected_astinletocaml_phrase=Js.to_ocamlToplevel_phrasephraseinif!Clflags.dump_parsetreethenPrintast.top_phraseppfocaml_phrase;if!Clflags.dump_sourcethenPprintast.top_phraseppfphrase;Toploop.execute_phrase!verboseppfocaml_phrase;;letcount_newlines:_Cst.tExpectation.Body.t->int=letcounts=String.counts~f:(Char.(=)'\n')infunction|Unreachable|Output->0|Exacts->counts|Prettycst->matchcstwith|Emptye->counte|Single_lines->counts.trailing_spaces|Multi_linesm->List.lengthm.lines-1+countm.leading_spaces+countm.trailing_spaces;;letcanonicalize_cst:'aCst.t->'aCst.t=function|Empty_->Empty"\n"|Single_lines->Multi_lines{leading_spaces="\n";trailing_spaces="\n";indentation="";lines=[Not_blank{trailing_blanks="";orig=s.orig;data=s.data}]}|Multi_linesm->Multi_lines{leading_spaces="\n";trailing_spaces="\n";indentation="";lines=List.mapm.lines~f:Cst.Line.strip};;letreconcile~actual~expect~allow_output_patterns:_Reconcile.Result.t=matchReconcile.expectation_body~expect~actual~default_indent:0~pad_single_line:false~allow_output_patternswith|Match->Match|Correctionc->Correction(Expectation.Body.map_prettyc~f:canonicalize_cst);;letredirect~f=letstdout_backup=Unix.dupUnix.stdoutinletstderr_backup=Unix.dupUnix.stderrinletfilename=Caml.Filename.temp_file"expect-test""stdout"inletfd_out=Unix.openfilefilename[O_WRONLY;O_CREAT;O_TRUNC]0o600inUnix.dup2fd_outUnix.stdout;Unix.dup2fd_outUnix.stderr;letic=In_channel.createfilenameinletread_up_to=ref0inletcapturebuf=Out_channel.flushstdout;Out_channel.flushstderr;letpos=Unix.lseekfd_out0SEEK_CURinletlen=pos-!read_up_toinread_up_to:=pos;Caml.Buffer.add_channelbuficleninprotect~f:(fun()->f~capture)~finally:(fun()->In_channel.closeic;Unix.closefd_out;Unix.dup2stdout_backupUnix.stdout;Unix.dup2stderr_backupUnix.stderr;Unix.closestdout_backup;Unix.closestderr_backup;Sys.removefilename);;typechunk_result=|Matched|Didn't_matchofFmt.tCst.tExpectation.Body.tleteval_expect_filefname~file_contents~capture~allow_output_patterns=(* 4.03: Warnings.reset_fatal (); *)letchunks,trailing_code=parse_contents~fnamefile_contents|>split_chunks~fname~allow_output_patternsinletbuf=Buffer.create1024inletppf=Format.formatter_of_bufferbufinreset_line_numbers:=false;line_numbers_delta:=0;letexec_phrasesphrases=(* So that [%expect_exact] nodes look nice *)Buffer.add_charbuf'\n';List.iterphrases~f:(funphrase->letsnap=Ocaml_common.Btype.snapshot()inmatchexec_phraseppfphrasewith|(_:bool)->()|exceptionexn->Location.report_exceptionppfexn;Ocaml_common.Btype.backtracksnap);Format.pp_print_flushppf();letlen=Buffer.lengthbufiniflen>0&&Buffer.nthbuf(len-1)<>'\n'then(* So that [%expect_exact] nodes look nice *)Buffer.add_charbuf'\n';capturebuf;ifBuffer.nthbuf(len-1)<>'\n'thenBuffer.add_charbuf'\n';lets=Buffer.contentsbufinBuffer.clearbuf;sinletresults=capture_compiler_stuffppf~f:(fun()->List.mapchunks~f:(funchunk->letactual=exec_phraseschunk.phrasesinmatchreconcile~actual~expect:chunk.expectation.body~allow_output_patternswith|Match->(chunk,actual,Matched)|Correctioncorrection->line_numbers_delta:=!line_numbers_delta+count_newlinescorrection-count_newlineschunk.expectation.body;(chunk,actual,Didn't_matchcorrection)))inlettrailing=matchtrailing_codewith|None->None|Some(phrases,pos_start,part)->letactual,result=capture_compiler_stuffppf~f:(fun()->letactual=exec_phrasesphrasesin(actual,reconcile~actual~expect:(PrettyCst.empty)~allow_output_patterns))inSome(pos_start,actual,result,part)in(results,trailing);;letinterpret_results_for_diffing~fname~file_contents(results,trailing)=letcorrections=List.filter_mapresults~f:(fun(chunk,_,result)->matchresultwith|Matched->None|Didn't_matchcorrection->Some(chunk.expectation,Matcher.Test_correction.Node_correction.Correctioncorrection))inlettrailing_output=matchtrailingwith|None->Reconcile.Result.Match|Some(_,_,correction,_)->correctioninMatcher.Test_correction.make~location:{filename=File.Name.of_stringfname;line_number=1;line_start=0;start_pos=0;end_pos=String.lengthfile_contents}~corrections~trailing_output~uncaught_exn:Match;;moduleT=Toplevel_expect_test_types(* Take a part of a file, trimming spaces at the beginning as well as ';;' *)letsub_filefile_contents~start~stop=letrecloopstart=ifstart>=stopthenstartelsematchfile_contents.[start]with|' '|'\t'|'\n'->loop(start+1)|';'whenstart+1<stop&&file_contents.[start+1]=';'->loop(start+2)|_->startinletstart=loopstartinString.subfile_contents~pos:start~len:(stop-start);;letgenerate_doc_for_sexp_output~fname:_~file_contents(results,trailing)=letrev_contents=List.rev_mapresults~f:(fun(chunk,resp,_)->letloc=chunk.phrases_locin(chunk.part,{T.Chunk.ocaml_code=sub_filefile_contents~start:loc.loc_start.pos_cnum~stop:loc.loc_end.pos_cnum;toplevel_response=resp}))inletrev_contents=matchtrailingwith|None->rev_contents|Some(pos_start,resp,_,part)->(part,{ocaml_code=sub_filefile_contents~start:pos_start.Lexing.pos_cnum~stop:(String.lengthfile_contents);toplevel_response=resp})::rev_contentsinletparts=List.group(List.revrev_contents)~break:(fun(a,_)(b,_)->a<>b)|>List.map~f:(functionchunks->{T.Part.name=Option.bind(List.hdchunks)~f:fst|>Option.value~default:"";chunks=List.mapchunks~f:snd})inletmatched=List.for_allresults~f:(fun(_,_,r)->r=Matched)&&matchtrailingwith|None|Some(_,_,Reconcile.Result.Match,_)->true|Some(_,_,Reconcile.Result.Correction_,_)->falsein{T.Document.parts;matched};;letdiff_command=refNoneletprocess_expect_filefname~use_color~in_place~sexp_output~use_absolute_path~allow_output_patterns=(* Captures the working directory before running the user code, which might change it *)letcwd=Sys.getcwd()inletfile_contents=In_channel.read_allfnameinletresult=redirect~f:(eval_expect_filefname~file_contents~allow_output_patterns)inifsexp_outputthenbeginletdoc=generate_doc_for_sexp_output~fname~file_contentsresultinFormat.printf"%a@."Sexp.pp_hum(T.Document.sexp_of_tdoc)end;letcorrected_fname=fname^".corrected"inletremove_corrected()=ifSys.file_existscorrected_fnamethenSys.removecorrected_fnameinmatchinterpret_results_for_diffing~fname~file_contentsresultwith|Correctioncorrection->Matcher.write_corrected[correction]~file:(ifin_placethenfnameelsecorrected_fname)~file_contents~mode:Toplevel_expect_test;ifin_placethenbeginremove_corrected();trueendelsebeginifnotsexp_outputthenbeginletmaybe_use_absolute_pathfile=ifuse_absolute_paththenFilename.concatcwdfileelsefileinPpxlib_print_diff.print()~file1:(maybe_use_absolute_pathfname)~file2:(maybe_use_absolute_pathcorrected_fname)~use_color?diff_command:!diff_commandend;falseend|Match->ifnotin_placethenremove_corrected();true;;letsetup_env()=(* Same as what run-tests.py does, to get repeatable output *)List.iter~f:(fun(k,v)->Unix.putenvkv)["LANG","C";"LC_ALL","C";"LANGUAGE","C";"TZ","GMT";"EMAIL","Foo Bar <foo.bar@example.com>";"CDPATH","";"COLUMNS","80";"GREP_OPTIONS","";"http_proxy","";"no_proxy","";"NO_PROXY","";"TERM","xterm"][%%ifocaml_version<(4,08,0)]letwarnings="@a-4-29-40-41-42-44-45-48-58"[%%else]letwarnings="@a-4-29-40-41-42-44-45-48-58-66"[%%endif]letsetup_config()=Clflags.real_paths:=false;Clflags.strict_sequence:=true;Clflags.strict_formats:=true;Clflags.unsafe_string:=Backend.unsafe_string();Warnings.parse_optionsfalsewarnings;;;letuse_color=reftrueletin_place=reffalseletsexp_output=reffalseletuse_absolute_path=reffalseletallow_output_patterns=reffalse[%%ifocaml_version<(4,09,0)]letinit_path()=Compmisc.init_pathtrue[%%else]letinit_path()=Compmisc.init_path()[%%endif]letmainfname=letcmd_line=Array.subSys.argv~pos:!Arg.current~len:(Array.lengthSys.argv-!Arg.current)insetup_env();setup_config();Core.Sys.override_argvcmd_line;Toploop.set_paths();init_path();Toploop.toplevel_env:=Compmisc.initial_env();Sys.interactive:=false;Backend.init();letsuccess=process_expect_filefname~use_color:!use_color~in_place:!in_place~sexp_output:!sexp_output~use_absolute_path:!use_absolute_path~allow_output_patterns:!allow_output_patternsinexit(ifsuccessthen0else1);;letargs=Arg.align["-no-color",Clearuse_color," Produce colored diffs";"-in-place",Setin_place," Overwirte file in place";"-diff-cmd",String(funs->diff_command:=Somes)," Diff command";"-sexp",Setsexp_output," Output the result as a s-expression instead of diffing";"-absolute-path",Setuse_absolute_path," Use absolute path in diff-error message";"-allow-output-patterns",Setallow_output_patterns," Allow output patterns in tests expectations";]letmain()=letusage=Printf.sprintf"Usage: %s [OPTIONS] FILE [ARGS]\n"(Filename.basenameSys.argv.(0))intryArg.parseargsmain(usage^"\nOptions are:");Out_channel.output_stringOut_channel.stderrusage;exit2withexn->Location.report_exceptionFormat.err_formatterexn;exit2;;