| // Copyright 2020 The Go Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style | 
 | // license that can be found in the LICENSE file. | 
 |  | 
 | package present | 
 |  | 
 | import ( | 
 | 	"bytes" | 
 | 	"html/template" | 
 | 	"os" | 
 | 	"os/exec" | 
 | 	"path/filepath" | 
 | 	"runtime" | 
 | 	"testing" | 
 | ) | 
 |  | 
 | func TestTestdata(t *testing.T) { | 
 | 	tmpl := template.Must(Template().Parse(testTmpl)) | 
 | 	filesP, err := filepath.Glob("testdata/*.p") | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	filesMD, err := filepath.Glob("testdata/*.md") | 
 | 	if err != nil { | 
 | 		t.Fatal(err) | 
 | 	} | 
 | 	files := append(filesP, filesMD...) | 
 | 	for _, file := range files { | 
 | 		file := file | 
 | 		name := filepath.Base(file) | 
 | 		if name == "README" { | 
 | 			continue | 
 | 		} | 
 | 		t.Run(name, func(t *testing.T) { | 
 | 			data, err := os.ReadFile(file) | 
 | 			if err != nil { | 
 | 				t.Fatalf("%s: %v", file, err) | 
 | 			} | 
 | 			marker := []byte("\n---\n") | 
 | 			i := bytes.Index(data, marker) | 
 | 			if i < 0 { | 
 | 				t.Fatalf("%s: cannot find --- marker in input", file) | 
 | 			} | 
 | 			input, html := data[:i+1], data[i+len(marker):] | 
 | 			doc, err := Parse(bytes.NewReader(input), name, 0) | 
 | 			if err != nil { | 
 | 				t.Fatalf("%s: %v", file, err) | 
 | 			} | 
 | 			var buf bytes.Buffer | 
 | 			if err := doc.Render(&buf, tmpl); err != nil { | 
 | 				t.Fatalf("%s: %v", file, err) | 
 | 			} | 
 | 			if !bytes.Equal(buf.Bytes(), html) { | 
 | 				diffText, err := diff("present-test-", "want", html, "have", buf.Bytes()) | 
 | 				if err != nil { | 
 | 					t.Fatalf("%s: diff: %v", file, err) | 
 | 				} | 
 | 				t.Errorf("%s: incorrect html:\n%s", file, diffText) | 
 | 			} | 
 | 		}) | 
 | 	} | 
 | } | 
 |  | 
 | func diff(prefix string, name1 string, b1 []byte, name2 string, b2 []byte) ([]byte, error) { | 
 | 	f1, err := writeTempFile(prefix, b1) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	defer os.Remove(f1) | 
 |  | 
 | 	f2, err := writeTempFile(prefix, b2) | 
 | 	if err != nil { | 
 | 		return nil, err | 
 | 	} | 
 | 	defer os.Remove(f2) | 
 |  | 
 | 	cmd := "diff" | 
 | 	if runtime.GOOS == "plan9" { | 
 | 		cmd = "/bin/ape/diff" | 
 | 	} | 
 |  | 
 | 	data, err := exec.Command(cmd, "-u", f1, f2).CombinedOutput() | 
 | 	if len(data) > 0 { | 
 | 		// diff exits with a non-zero status when the files don't match. | 
 | 		// Ignore that failure as long as we get output. | 
 | 		err = nil | 
 | 	} | 
 |  | 
 | 	data = bytes.Replace(data, []byte(f1), []byte(name1), -1) | 
 | 	data = bytes.Replace(data, []byte(f2), []byte(name2), -1) | 
 |  | 
 | 	return data, err | 
 | } | 
 |  | 
 | func writeTempFile(prefix string, data []byte) (string, error) { | 
 | 	file, err := os.CreateTemp("", prefix) | 
 | 	if err != nil { | 
 | 		return "", err | 
 | 	} | 
 | 	_, err = file.Write(data) | 
 | 	if err1 := file.Close(); err == nil { | 
 | 		err = err1 | 
 | 	} | 
 | 	if err != nil { | 
 | 		os.Remove(file.Name()) | 
 | 		return "", err | 
 | 	} | 
 | 	return file.Name(), nil | 
 | } | 
 |  | 
 | var testTmpl = ` | 
 | {{define "root" -}} | 
 | <h1>{{.Title}}</h1> | 
 | {{with .Subtitle}}<h2>{{.}}</h2> | 
 | {{end -}} | 
 | {{range .Authors}}<author> | 
 | {{range .Elem}}{{elem $.Template .}}{{end}}</author> | 
 | {{end -}} | 
 | {{range .Sections}}<section>{{elem $.Template .}}</section> | 
 | {{end -}} | 
 | {{end}} | 
 |  | 
 | {{define "newline"}}{{/* No automatic line break. Paragraphs are free-form. */}} | 
 | {{end}} | 
 |  | 
 | {{define "section"}} | 
 | {{if .Title}}<h2 id="TOC_{{.FormattedNumber}}">{{.Title}}</h2> | 
 | {{end -}} | 
 | {{range .Elem}}{{elem $.Template .}}{{end}} | 
 | {{- end}} | 
 |  | 
 | {{define "list" -}} | 
 | <ul> | 
 | {{range .Bullet -}} | 
 | <li>{{style .}}</li> | 
 | {{end -}} | 
 | </ul> | 
 | {{end}} | 
 |  | 
 | {{define "text" -}} | 
 | {{if .Pre -}} | 
 | <pre>{{range .Lines}}{{.}}{{end}}</pre> | 
 | {{else -}} | 
 | <p>{{range $i, $l := .Lines}}{{if $i}}{{template "newline"}}{{end}}{{style $l}}{{end}}</p> | 
 | {{end -}} | 
 | {{end}} | 
 |  | 
 | {{define "code" -}} | 
 | {{if .Play -}} | 
 | <div class="playground">{{.Text}}</div> | 
 | {{else -}} | 
 | <div class="code">{{.Text}}</div> | 
 | {{end -}} | 
 | {{end}} | 
 |  | 
 | {{define "image" -}} | 
 | <img src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}} alt=""> | 
 | {{end}} | 
 |  | 
 | {{define "caption" -}} | 
 | <figcaption>{{style .Text}}</figcaption> | 
 | {{end}} | 
 |  | 
 | {{define "iframe" -}} | 
 | <iframe src="{{.URL}}"{{with .Height}} height="{{.}}"{{end}}{{with .Width}} width="{{.}}"{{end}}></iframe> | 
 | {{end}} | 
 |  | 
 | {{define "link" -}} | 
 | <p class="link"><a href="{{.URL}}">{{style .Label}}</a></p> | 
 | {{end}} | 
 |  | 
 | {{define "html" -}}{{.HTML}}{{end}} | 
 | ` |