github.com/ubuntu/ubuntu-report@v1.7.4-0.20240410144652-96f37d845fac/cmd/ubuntu-report/generate_test.go (about) 1 package main 2 3 import ( 4 "bufio" 5 "flag" 6 "io" 7 "io/ioutil" 8 "os" 9 "path/filepath" 10 "strings" 11 "testing" 12 13 "github.com/pkg/errors" 14 "github.com/spf13/cobra" 15 "github.com/spf13/cobra/doc" 16 "github.com/ubuntu/ubuntu-report/internal/helper" 17 ) 18 19 /* 20 * using test file for manpage and bash completion generate so that 21 * we don't embed the code and dependencies in final binary 22 */ 23 24 var generate = flag.Bool("generate", false, "generate manpages and completion files") 25 var out = flag.String("path", "build", "custom directory where to generate files when using --generate") 26 27 func TestGenerateManpage(t *testing.T) { 28 if !*generate { 29 helper.SkipIfShort(t) 30 t.Log("mocking man page generation, --generate isn't set") 31 _, tearDown := chTempDir(t) 32 defer tearDown() 33 } else { 34 t.Parallel() 35 } 36 37 if err := os.Mkdir(*out, 0755); err != nil && os.IsNotExist(err) { 38 t.Fatalf("couldn't create %s directory: %v", *out, err) 39 } 40 header := &doc.GenManHeader{ 41 Title: "Ubuntu Report", 42 Section: "3", 43 } 44 if err := doc.GenManTree(generateRootCmd(), header, *out); err != nil { 45 t.Fatalf("couldn't generate manpage: %v", err) 46 } 47 } 48 49 func TestGenerateCompletion(t *testing.T) { 50 if !*generate { 51 helper.SkipIfShort(t) 52 t.Log("mocking bash and zsh completion generation, --generate isn't set") 53 _, tearDown := chTempDir(t) 54 defer tearDown() 55 } else { 56 t.Parallel() 57 } 58 59 rootCmd := generateRootCmd() 60 if err := os.Mkdir(*out, 0755); err != nil && os.IsNotExist(err) { 61 t.Fatalf("couldn't create %s directory: %v", *out, err) 62 } 63 if err := rootCmd.GenBashCompletionFile(filepath.Join(*out, "bash-completion")); err != nil { 64 t.Fatalf("couldn't generate bash completion: %v", err) 65 } 66 if err := rootCmd.GenZshCompletionFile(filepath.Join(*out, "zsh-completion")); err != nil { 67 t.Fatalf("couldn't generate bazshsh completion: %v", err) 68 } 69 } 70 71 func TestGenerateREADME(t *testing.T) { 72 sp := filepath.Join("..", "..", "README.md") 73 if _, err := os.Stat(sp); err != nil && os.IsNotExist(err) { 74 t.Skip("no README.md file: doesn't run from source repository, nothing to update") 75 } 76 if !*generate { 77 helper.SkipIfShort(t) 78 t.Log("mock README generation, --generate isn't set") 79 sp = filepath.Join(curDir(t), sp) 80 d, tearDown := chTempDir(t) 81 defer tearDown() 82 83 newsp := filepath.Join(d, "README.md") 84 helper.CopyFile(t, sp, newsp) 85 sp = newsp 86 } else { 87 t.Parallel() 88 } 89 90 dp := sp + ".new" 91 src, err := os.Open(sp) 92 if err != nil { 93 t.Fatalf("couldn't open %s: %v", sp, err) 94 } 95 defer src.Close() 96 dst, err := os.Create(dp) 97 if err != nil { 98 t.Fatalf("couldn't create %s: %v", dp, err) 99 } 100 defer dst.Close() 101 102 // write start of README 103 scanner := bufio.NewScanner(src) 104 for scanner.Scan() { 105 txt := scanner.Text() 106 mustWrite(t, dst, txt) 107 if txt == "## Command line usage" { 108 mustWrite(t, dst, "") 109 break 110 } 111 } 112 113 // write generated command line 114 cmds := []*cobra.Command{generateRootCmd()} 115 cmds = append(cmds, cmds[0].Commands()...) 116 for _, cmd := range cmds { 117 pr, pw := io.Pipe() 118 119 go func() { 120 if err := doc.GenMarkdown(cmd, pw); err != nil { 121 pw.CloseWithError(errors.Wrapf(err, "generate markdown for %s failed", cmd.Name())) 122 return 123 } 124 pw.Close() 125 }() 126 127 mScanner := bufio.NewScanner(pr) 128 for mScanner.Scan() { 129 txt := mScanner.Text() 130 if strings.HasPrefix(txt, "### SEE ALSO") { 131 break 132 } 133 // interactive is an alias to ubuntu-report only, don't file up more info 134 if cmd.Name() == "interactive" && strings.HasPrefix(txt, "### Synopsis") { 135 break 136 } 137 // add a subindentation 138 if strings.HasPrefix(txt, "##") { 139 txt = "#" + txt 140 } 141 mustWrite(t, dst, txt) 142 } 143 144 if err = mScanner.Err(); err != nil { 145 t.Fatalf("error while reading generated markdown for %s: %v", cmd.Name(), err) 146 } 147 } 148 149 // skip to next paragraph (ignore previous generation) and write to the end of file 150 skip := true 151 for scanner.Scan() { 152 txt := scanner.Text() 153 if skip && strings.HasPrefix(txt, "## ") { 154 skip = false 155 } 156 if !skip { 157 mustWrite(t, dst, txt) 158 } 159 } 160 if err = scanner.Err(); err != nil { 161 t.Fatalf("error while reading %s: %v", sp, err) 162 } 163 164 dst.Close() 165 if err = os.Rename(dp, sp); err != nil { 166 t.Fatalf("couldn't rename %s to %s: %v", dp, sp, err) 167 } 168 } 169 170 func mustWrite(t *testing.T, f *os.File, s string) { 171 if _, err := f.WriteString(s + "\n"); err != nil { 172 t.Fatalf("couldn't write '%s' to %s: %v", s, f.Name(), err) 173 } 174 } 175 176 func curDir(t *testing.T) string { 177 t.Helper() 178 179 c, err := os.Getwd() 180 if err != nil { 181 t.Fatal("couldn't get current directory", err) 182 } 183 return c 184 } 185 186 func chTempDir(t *testing.T) (string, func()) { 187 t.Helper() 188 d, err := ioutil.TempDir("", "ubuntu-report-tests") 189 if err != nil { 190 t.Fatal("couldn't create temporary directory", err) 191 } 192 c := curDir(t) 193 if err = os.Chdir(d); err != nil { 194 t.Fatalf("couldn't change directory to %s: %v", d, err) 195 } 196 197 return d, func() { 198 if err = os.Chdir(c); err != nil { 199 t.Fatalf("couldn't restore to initial directory %s, %v", c, err) 200 } 201 if err = os.RemoveAll(d); err != nil { 202 t.Fatalf("couldn't clean temporary directory %s, %v", d, err) 203 } 204 } 205 }