| 1 | // vtest retry: 3 |
| 2 | import os |
| 3 | import rand |
| 4 | import term |
| 5 | import v.util.vtest |
| 6 | import v.util.diff |
| 7 | |
| 8 | const vexe_path = @VEXE |
| 9 | const vexe = os.quoted_path(vexe_path) |
| 10 | const vroot = os.dir(vexe_path) |
| 11 | const should_autofix = os.getenv('VAUTOFIX') != '' |
| 12 | |
| 13 | fn test_output() { |
| 14 | os.setenv('VCOLORS', 'never', true) |
| 15 | os.chdir(vroot)! |
| 16 | mut total_fails := 0 |
| 17 | test_files := vtest.filter_vtest_only(os.walk_ext('cmd/tools/vdoc/testdata', '.v'), |
| 18 | basepath: vroot |
| 19 | ) |
| 20 | for path in test_files { |
| 21 | mut fails := 0 |
| 22 | qpath := os.quoted_path(path) |
| 23 | path_no_ext := path.all_before_last('.') |
| 24 | print(path + ' ') |
| 25 | fails += check_output('${vexe} doc ${qpath}', path_no_ext + '.out') |
| 26 | fails += check_output('${vexe} doc -comments ${qpath}', '${path_no_ext}.unsorted.out', |
| 27 | should_sort: false |
| 28 | ) |
| 29 | fails += check_output('${vexe} doc -comments ${qpath}', '${path_no_ext}.comments.out') |
| 30 | fails += check_output('${vexe} doc -readme -comments ${qpath}', |
| 31 | '${path_no_ext}.readme.comments.out') |
| 32 | // test the main 3 different formats: |
| 33 | program_dir := os.quoted_path(if os.is_dir(path) { path } else { os.dir(path) }) |
| 34 | for fmt in ['html', 'ansi', 'text'] { |
| 35 | fails += check_output('${vexe} doc -no-timestamp -f ${fmt} -o - -html-only-contents -readme -comments ${program_dir}', |
| 36 | '${path_no_ext}.${fmt}') |
| 37 | } |
| 38 | fails += check_output('${vexe} doc -no-timestamp -f md -o - ${program_dir}', |
| 39 | '${path_no_ext}.md') |
| 40 | if fails == 0 { |
| 41 | println(term.green('OK')) |
| 42 | } else { |
| 43 | total_fails += fails |
| 44 | } |
| 45 | flush_stdout() |
| 46 | } |
| 47 | assert total_fails == 0 |
| 48 | } |
| 49 | |
| 50 | const small_pure_v_vlib_module = 'bitfield' |
| 51 | |
| 52 | fn test_out_path() { |
| 53 | // Work around CI issues covering v doc generation for relative input paths in tmp dir. |
| 54 | // Instead just generate documentation in the v source dir. |
| 55 | if os.getenv('CI') == 'true' { |
| 56 | default_output_path := os.join_path(vroot, 'vlib', small_pure_v_vlib_module, '_docs') |
| 57 | os.execute_opt('${vexe} doc -f html -m vlib/${small_pure_v_vlib_module}')! |
| 58 | final_html_path := os.join_path(default_output_path, '${small_pure_v_vlib_module}.html') |
| 59 | assert os.exists(final_html_path), final_html_path |
| 60 | |
| 61 | // Custom out path (no `_docs` subdir). |
| 62 | out_dir := os.join_path(vroot, 'vlib', small_pure_v_vlib_module, 'docs') |
| 63 | os.execute_opt('${vexe} doc -f html -m -o ${out_dir} ${small_pure_v_vlib_module}')! |
| 64 | out_html_path := os.join_path(out_dir, '${small_pure_v_vlib_module}.html') |
| 65 | assert os.exists(out_html_path), out_html_path |
| 66 | os.rmdir_all(out_dir) or {} |
| 67 | os.rmdir_all(default_output_path) or {} |
| 68 | return |
| 69 | } |
| 70 | |
| 71 | // Copy a *small* vlib module, that is written in pure V, for the test: |
| 72 | test_path := os.join_path(os.vtmp_dir(), 'vdoc_test_${rand.ulid()}') |
| 73 | test_mod_path := os.join_path(test_path, small_pure_v_vlib_module) |
| 74 | os.mkdir_all(test_path)! |
| 75 | // Sentinel v.mod so that vdoc's get_parent_mod() stops climbing here instead of |
| 76 | // picking up stray .v files in ancestor tmp dirs (e.g. /private/tmp on macOS). |
| 77 | os.write_file(os.join_path(test_path, 'v.mod'), "Module { name: 'vdoc_test' }\n")! |
| 78 | defer { |
| 79 | os.chdir(vroot) or {} |
| 80 | os.rmdir_all(test_path) or {} |
| 81 | } |
| 82 | os.chdir(test_path)! |
| 83 | mod_path := os.join_path(vroot, 'vlib', small_pure_v_vlib_module) |
| 84 | os.cp_all(mod_path, test_mod_path, true) or {} |
| 85 | |
| 86 | // Relative input with default output path. |
| 87 | os.execute_opt('${vexe} doc -f html -m ${small_pure_v_vlib_module}')! |
| 88 | output_path := os.join_path(test_mod_path, '_docs', '${small_pure_v_vlib_module}.html') |
| 89 | assert os.exists(output_path), output_path |
| 90 | |
| 91 | // Custom out path (no `_docs` subdir). |
| 92 | out_dir := os.join_path(os.vtmp_dir(), 'docs_test') |
| 93 | defer { |
| 94 | os.rmdir_all(out_dir) or {} |
| 95 | } |
| 96 | os.execute_opt('${vexe} doc -f html -m -o ${out_dir} ${small_pure_v_vlib_module}')! |
| 97 | html_path := os.join_path(out_dir, '${small_pure_v_vlib_module}.html') |
| 98 | assert os.exists(html_path), html_path |
| 99 | } |
| 100 | |
| 101 | fn print_compare(expected string, found string) { |
| 102 | println(term.red('FAIL')) |
| 103 | println('============') |
| 104 | if diff_ := diff.compare_text(expected, found) { |
| 105 | println('diff:') |
| 106 | println(diff_) |
| 107 | println('============\n') |
| 108 | } else { |
| 109 | println('expected:') |
| 110 | println(expected) |
| 111 | println('============') |
| 112 | println('found:') |
| 113 | println(found) |
| 114 | println('============\n') |
| 115 | } |
| 116 | } |
| 117 | |
| 118 | @[params] |
| 119 | struct CheckOutputParams { |
| 120 | should_sort bool = true |
| 121 | } |
| 122 | |
| 123 | fn check_output(cmd string, out_path string, opts CheckOutputParams) int { |
| 124 | if !os.exists(out_path) { |
| 125 | return 0 |
| 126 | } |
| 127 | mut fails := 0 |
| 128 | os.setenv('VDOC_SORT', opts.should_sort.str(), true) |
| 129 | expected := os.read_file(out_path) or { panic(err) }.replace('\r\n', '\n').trim_space() |
| 130 | res := os.execute_opt(cmd) or { panic(err) } |
| 131 | found := res.output.replace('\r\n', '\n').trim_space() |
| 132 | if expected != found { |
| 133 | print_compare(expected, found) |
| 134 | eprintln('>>> cmd: VDOC_SORT=${opts.should_sort} ${cmd}') |
| 135 | eprintln('>>> out_file_path: `${out_path}`') |
| 136 | eprintln('>>> fix: VDOC_SORT=${opts.should_sort} ${cmd} > ${out_path}') |
| 137 | fails++ |
| 138 | } |
| 139 | if should_autofix { |
| 140 | os.write_file(out_path, res.output) or {} |
| 141 | } |
| 142 | return fails |
| 143 | } |
| 144 | |