| 1 | // Copyright 2020 The Go Authors. All rights reserved. |
|---|---|
| 2 | // Use of this source code is governed by a BSD-style |
| 3 | // license that can be found in the LICENSE file. |
| 4 | |
| 5 | package present |
| 6 | |
| 7 | import ( |
| 8 | "bytes" |
| 9 | "html/template" |
| 10 | "io/ioutil" |
| 11 | "os" |
| 12 | "os/exec" |
| 13 | "path/filepath" |
| 14 | "runtime" |
| 15 | "testing" |
| 16 | ) |
| 17 | |
| 18 | func TestTestdata(t *testing.T) { |
| 19 | tmpl := template.Must(Template().Parse(testTmpl)) |
| 20 | filesP, err := filepath.Glob("testdata/*.p") |
| 21 | if err != nil { |
| 22 | t.Fatal(err) |
| 23 | } |
| 24 | filesMD, err := filepath.Glob("testdata/*.md") |
| 25 | if err != nil { |
| 26 | t.Fatal(err) |
| 27 | } |
| 28 | files := append(filesP, filesMD...) |
| 29 | for _, file := range files { |
| 30 | file := file |
| 31 | name := filepath.Base(file) |
| 32 | if name == "README" { |
| 33 | continue |
| 34 | } |
| 35 | t.Run(name, func(t *testing.T) { |
| 36 | data, err := ioutil.ReadFile(file) |
| 37 | if err != nil { |
| 38 | t.Fatalf("%s: %v", file, err) |
| 39 | } |
| 40 | marker := []byte("\n---\n") |
| 41 | i := bytes.Index(data, marker) |
| 42 | if i < 0 { |
| 43 | t.Fatalf("%s: cannot find --- marker in input", file) |
| 44 | } |
| 45 | input, html := data[:i+1], data[i+len(marker):] |
| 46 | doc, err := Parse(bytes.NewReader(input), name, 0) |
| 47 | if err != nil { |
| 48 | t.Fatalf("%s: %v", file, err) |
| 49 | } |
| 50 | var buf bytes.Buffer |
| 51 | if err := doc.Render(&buf, tmpl); err != nil { |
| 52 | t.Fatalf("%s: %v", file, err) |
| 53 | } |
| 54 | if !bytes.Equal(buf.Bytes(), html) { |
| 55 | diffText, err := diff("present-test-", "want", html, "have", buf.Bytes()) |
| 56 | if err != nil { |
| 57 | t.Fatalf("%s: diff: %v", file, err) |
| 58 | } |
| 59 | t.Errorf("%s: incorrect html:\n%s", file, diffText) |
| 60 | } |
| 61 | }) |
| 62 | } |
| 63 | } |
| 64 | |
| 65 | func diff(prefix string, name1 string, b1 []byte, name2 string, b2 []byte) ([]byte, error) { |
| 66 | f1, err := writeTempFile(prefix, b1) |
| 67 | if err != nil { |
| 68 | return nil, err |
| 69 | } |
| 70 | defer os.Remove(f1) |
| 71 | |
| 72 | f2, err := writeTempFile(prefix, b2) |
| 73 | if err != nil { |
| 74 | return nil, err |
| 75 | } |
| 76 | defer os.Remove(f2) |
| 77 | |
| 78 | cmd := "diff" |
| 79 | if runtime.GOOS == "plan9" { |
| 80 | cmd = "/bin/ape/diff" |
| 81 | } |
| 82 | |
| 83 | data, err := exec.Command(cmd, "-u", f1, f2).CombinedOutput() |
| 84 | if len(data) > 0 { |
| 85 | // diff exits with a non-zero status when the files don't match. |
| 86 | // Ignore that failure as long as we get output. |
| 87 | err = nil |
| 88 | } |
| 89 | |
| 90 | data = bytes.Replace(data, []byte(f1), []byte(name1), -1) |
| 91 | data = bytes.Replace(data, []byte(f2), []byte(name2), -1) |
| 92 | |
| 93 | return data, err |
| 94 | } |
| 95 | |
| 96 | func writeTempFile(prefix string, data []byte) (string, error) { |
| 97 | file, err := ioutil.TempFile("", prefix) |
| 98 | if err != nil { |
| 99 | return "", err |
| 100 | } |
| 101 | _, err = file.Write(data) |
| 102 | if err1 := file.Close(); err == nil { |
| 103 | err = err1 |
| 104 | } |
| 105 | if err != nil { |
| 106 | os.Remove(file.Name()) |
| 107 | return "", err |
| 108 | } |
| 109 | return file.Name(), nil |
| 110 | } |
| 111 | |
| 112 | var testTmpl = ` |
| 113 | {{define "root" -}} |
| 114 | <h1>{{.Title}}</h1> |
| 115 | {{with .Subtitle}}<h2>{{.}}</h2> |
| 116 | {{end -}} |
| 117 | {{range .Authors}}<author> |
| 118 | {{range .Elem}}{{elem $.Template .}}{{end}}</author> |
| 119 | {{end -}} |
| 120 | {{range .Sections}}<section>{{elem $.Template .}}</section> |
| 121 | {{end -}} |
| 122 | {{end}} |
| 123 | |
| 124 | {{define "newline"}}{{/* No automatic line break. Paragraphs are free-form. */}} |
| 125 | {{end}} |
| 126 | |
| 127 | {{define "section"}} |
| 128 | {{if .Title}}<h2 id="TOC_{{.FormattedNumber}}">{{.Title}}</h2> |
| 129 | {{end -}} |
| 130 | {{range .Elem}}{{elem $.Template .}}{{end}} |
| 131 | {{- end}} |
| 132 | |
| 133 | {{define "list" -}} |
| 134 | <ul> |
| 135 | {{range .Bullet -}} |
| 136 | <li>{{style .}}</li> |
| 137 | {{end -}} |
| 138 | </ul> |
| 139 | {{end}} |
| 140 | |
| 141 | {{define "text" -}} |
| 142 | {{if .Pre -}} |
| 143 | <pre>{{range .Lines}}{{.}}{{end}}</pre> |
| 144 | {{else -}} |
| 145 | <p>{{range $i, $l := .Lines}}{{if $i}}{{template "newline"}}{{end}}{{style $l}}{{end}}</p> |
| 146 | {{end -}} |
| 147 | {{end}} |
| 148 | |
| 149 | {{define "code" -}} |
| 150 | {{if .Play -}} |
| 151 | <div class="playground">{{.Text}}</div> |
| 152 | {{else -}} |
| 153 | <div class="code">{{.Text}}</div> |
| 154 | {{end -}} |
| 155 | {{end}} |
| 156 | |
| 157 | {{define "image" -}} |
| 158 | <img src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}} alt=""> |
| 159 | {{end}} |
| 160 | |
| 161 | {{define "caption" -}} |
| 162 | <figcaption>{{style .Text}}</figcaption> |
| 163 | {{end}} |
| 164 | |
| 165 | {{define "iframe" -}} |
| 166 | <iframe src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}}></iframe> |
| 167 | {{end}} |
| 168 | |
| 169 | {{define "link" -}} |
| 170 | <p class="link"><a href="{{.URL}}">{{style .Label}}</a></p> |
| 171 | {{end}} |
| 172 | |
| 173 | {{define "html" -}}{{.HTML}}{{end}} |
| 174 | ` |
| 175 |
Members