git/orchard/main
tui/internal/ui/layout_fuzz_test.go
138 lines4 KB last change raw · history
package ui

import (
	"strings"
	"testing"

	"updater-tui/internal/plan"
)

// adversarial strings that a PKGBUILD could genuinely contain in a description,
// a source URL or an audit evidence line
var ugly = []string{
	"日本語のパッケージ説明",
	"🎉🚀 emoji in a description",
	"combining: e\u0301\u0301\u0301\u0301",
	"zero\u200bwidth\u200bspaces",
	"RTL \u202eoverride\u202c text",
	"a\tb\tc",
	strings.Repeat("x", 300),
	"",
	"\x1b[31mraw escape\x1b[0m",
	"ff ligature and fi",
	"👨‍👩‍👧‍👦 family zwj",
}

func TestRowsNeverOverflowWithUnicodeContent(t *testing.T) {
	for _, s := range ugly {
		doc := &plan.Plan{
			Aur: []plan.AurUpdate{{Name: "pkg", Old: "1-1", New: "2-1", Tag: "!!",
				Note: s}},
			Repo:   []plan.Upgrade{{Name: "r", Old: s, New: s, Delta: -1}},
			Vcs:    []plan.Vcs{{Name: "v-git", Old: s, New: s}},
			Checks: []plan.Check{{OK: false, Label: s, Detail: s}},
			Audits: map[string][]plan.Finding{
				"pkg": {{Rule: s, Severity: "BLOCK", Title: s, Evidence: s}},
			},
			Advisories: []plan.Advisory{{ID: s, Severity: s, Fixed: s,
				Packages: []string{s}, Issues: []string{s}}},
		}
		for _, width := range []int{60, 80, 100, 160} {
			m := loaded(t, doc, width, 40)
			m.modal = nil
			for i, line := range strings.Split(m.View(), "\n") {
				if got := displayWidth(line); got > width {
					t.Errorf("width=%d content=%q: line %d is %d cells: %q",
						width, s, i, got, plain(line))
				}
			}
			// and every overlay must survive the same content
			for _, show := range []string{"help", "confirm"} {
				m2 := loaded(t, doc, width, 40)
				switch show {
				case "help":
					m2.modal = newHelp()
				case "confirm":
					updated, _ := m2.confirmApply()
					m2 = updated.(Model)
				}
				if m2.modal == nil {
					continue
				}
				for i, line := range strings.Split(m2.modal.View(width, 40), "\n") {
					if got := displayWidth(line); got > width {
						t.Errorf("%s width=%d content=%q: line %d is %d cells: %q",
							show, width, s, i, got, plain(line))
					}
				}
			}
		}
	}
}

func TestDetailOverlayNeverOverflows(t *testing.T) {
	for _, s := range ugly {
		d := &plan.Detail{
			Package: s, Installed: s, Available: s, BaselineNote: s,
			Diff: plan.Diff{Added: 1, Removed: 1, Lines: []plan.DiffLine{
				{Kind: "add", Text: s}, {Kind: "del", Text: s}, {Kind: "gap"},
				{Kind: "ctx", Text: s}}},
			Findings:   []plan.Finding{{Rule: s, Severity: "BLOCK", Title: s, Evidence: s}},
			Advisories: []plan.Advisory{{ID: s, Severity: s, Fixed: s, Issues: []string{s}}},
			Pkgbuild:   []string{s, s},
			Meta:       plan.Meta{Maintainer: s, URL: s, Depends: []string{s}},
		}
		for _, width := range []int{60, 100, 160} {
			for tab := 0; tab < 4; tab++ {
				modal := newDetail("pkg", width)
				modal.Update(detailMsg{pkg: "pkg", detail: d})
				modal.tab = tab
				modal.built = false
				for i, line := range strings.Split(modal.View(width, 40), "\n") {
					if got := displayWidth(line); got > width {
						t.Errorf("tab=%d width=%d content=%q: line %d is %d cells",
							tab, width, s, i, got)
					}
				}
			}
		}
	}
}

func TestTextAndAdvisoryModalsNeverOverflow(t *testing.T) {
	for _, s := range ugly {
		for _, lines := range [][]string{
			advisoryLines(&plan.AdvisoryDetail{ID: s, Available: true, Severity: s,
				Kind: s, Affected: s, Fixed: s, Packages: []string{s},
				Issues: []string{s, s}, References: []string{s}, Notes: s, URL: s}),
			securityLines(&plan.SecurityReport{Available: true, Total: 1, Fixable: 1,
				Advisories: []plan.Advisory{{ID: s, Severity: s, Fixed: s,
					Packages: []string{s}, Issues: []string{s}}}}),
		} {
			for _, width := range []int{60, 100, 160} {
				m := newText("t")
				m.Update(textBodyMsg{title: "t", lines: lines})
				for i, line := range strings.Split(m.View(width, 40), "\n") {
					if got := displayWidth(line); got > width {
						t.Errorf("width=%d content=%q: line %d is %d cells",
							width, s, i, got)
					}
				}
			}
		}
	}
}

func TestLoadingViewNeverOverflows(t *testing.T) {
	for _, width := range []int{50, 60, 100} {
		m := New(plan.Backend{}, testConfig(t))
		m.width, m.height = width, 20
		m.status = "日本語のとても長いステータスメッセージ"
		for i, line := range strings.Split(m.View(), "\n") {
			if got := displayWidth(line); got > width {
				t.Errorf("width=%d: line %d is %d cells: %q", width, i, got, plain(line))
			}
		}
	}
}