package gallery import ( "path" "reflect" "sort" "strings" "testing" "amadan.net/rastrillo/fiesta" ) // maxPageBytes is the most one HTML file of the tree may weigh. Fiesta // has no debt table: every page is under it, and a page that is not // fails by name. const maxPageBytes = 128 << 10 // siteMount is where the site serves the tree, so the pages weighed // are the pages a reader is sent, URLs and all. const siteMount = "/design-system" // minTreeHTML is the fewest HTML files a whole tree holds: thirty-six // directories, three themes by twelve locales, of at least a hundred // pages and frame documents each. A render that came back empty, or // with a theme or a locale missing, is under it, so neither gate below // can pass by reading nothing. const minTreeHTML = 36 * 100 // htmlFiles counts the HTML files in a tree. func htmlFiles(files map[string][]byte) int { n := 0 for name := range files { if strings.HasSuffix(name, ".html") { n++ } } return n } // TestEveryPageStaysUnderItsBudget: a page's weight is what a reader // waits for, so it is held per page, not in total: every .html file of // the tree, pages and frame documents alike, is at most 128 KiB. The // tree's total is logged, being worth knowing and worth nothing as a // ceiling. Ported from Rastrillo without its debt table, which Fiesta // has no entry for. // // Already passing when written. The mutation that turns it red is // 200 KiB of ballast in pageTemplate's head, a meta element's content, // which puts every gallery page over. Not a comment: html/template // drops comments from a template's text, so a comment weighs nothing. func TestEveryPageStaysUnderItsBudget(t *testing.T) { files := render(t, siteMount) if n := htmlFiles(files); n < minTreeHTML { t.Fatalf("the tree holds %d HTML files; a whole one holds at least %d, so this would weigh too little to mean anything", n, minTreeHTML) } var total, heaviest int var heaviestName string var over []string for name, body := range files { total += len(body) if path.Ext(name) != ".html" { continue } if len(body) > heaviest { heaviest, heaviestName = len(body), name } if len(body) > maxPageBytes { over = append(over, name) } } sort.Strings(over) for i, name := range over { if i == 10 { t.Errorf("… and %d more over the %d-byte budget", len(over)-i, maxPageBytes) break } t.Errorf("%s is %d bytes, over its %d-byte budget", name, len(files[name]), maxPageBytes) } t.Logf("heaviest page: %s at %d bytes; whole tree: %d files (%d HTML), %d bytes (%.2f MiB)", heaviestName, heaviest, len(files), htmlFiles(files), total, float64(total)/(1<<20)) } // TestRenderIsDeterministic: two renders of the same source are byte // for byte the same, file for file. Every map on the way to the output // has to be read in a fixed order; without this gate a map's random // order would show as a different byte stream on every build of the // site, in thousands of files, for no change at all. The first render // is the package's shared one at the site's mount, the second a fresh // one, and each must be a whole tree. // // Already passing when written. The mutation that turns it red is a // time.Now() in a template, a "now" func printed into pageTemplate's // head: every gallery page then differs between the two renders. func TestRenderIsDeterministic(t *testing.T) { first := render(t, siteMount) second, err := Render(fiesta.FS(), Options{Mount: siteMount}) if err != nil { t.Fatalf("Render: %v", err) } for i, files := range []map[string][]byte{first, second} { if n := htmlFiles(files); n < minTreeHTML { t.Fatalf("render %d holds %d HTML files; a whole tree holds at least %d, so two empty renders would agree", i+1, n, minTreeHTML) } } if reflect.DeepEqual(first, second) { t.Logf("two renders of %d files agree byte for byte", len(first)) return } var diffs []string for name, a := range first { b, ok := second[name] switch { case !ok: diffs = append(diffs, "only in the first: "+name) case string(a) != string(b): diffs = append(diffs, "differs: "+name) } } for name := range second { if _, ok := first[name]; !ok { diffs = append(diffs, "only in the second: "+name) } } sort.Strings(diffs) if len(diffs) > 20 { diffs = append(diffs[:20], "…") } t.Fatalf("Render is not deterministic:\n%s", strings.Join(diffs, "\n")) }