bastion 0.53.2
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"rsc.io/qr"
|
||||
)
|
||||
|
||||
const qrTestURI = "otpauth://totp/Privatae%20NAC%3AWORKSTATION-01%20for%20member" +
|
||||
"?secret=JBSWY3DPEHPK3PXPJBSWY3DPEHPK3PXP&issuer=Privatae+NAC&digits=6&period=30"
|
||||
|
||||
// parseRendered turns qrTerminal's output back into a module grid, so the test
|
||||
// asserts against what a CAMERA would see rather than against the library call.
|
||||
// The packing and the quiet zone are this repo's code; a bug there produces a
|
||||
// block that looks perfectly fine on screen and never scans, which is exactly
|
||||
// the failure this feature exists to remove.
|
||||
func parseRendered(t *testing.T, s string) [][]bool {
|
||||
t.Helper()
|
||||
var rows [][]bool
|
||||
for _, line := range strings.Split(s, "\n") {
|
||||
// strip the SGR sequences
|
||||
for {
|
||||
i := strings.Index(line, "\x1b[")
|
||||
if i < 0 {
|
||||
break
|
||||
}
|
||||
j := strings.Index(line[i:], "m")
|
||||
if j < 0 {
|
||||
break
|
||||
}
|
||||
line = line[:i] + line[i+j+1:]
|
||||
}
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
var top, bot []bool
|
||||
for _, ch := range line {
|
||||
switch ch {
|
||||
case '█':
|
||||
top, bot = append(top, true), append(bot, true)
|
||||
case '▀':
|
||||
top, bot = append(top, true), append(bot, false)
|
||||
case '▄':
|
||||
top, bot = append(top, false), append(bot, true)
|
||||
default:
|
||||
top, bot = append(top, false), append(bot, false)
|
||||
}
|
||||
}
|
||||
rows = append(rows, top, bot)
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
func TestQRTerminalRoundTrip(t *testing.T) {
|
||||
out, err := qrTerminal(qrTestURI)
|
||||
if err != nil {
|
||||
t.Fatalf("qrTerminal: %v", err)
|
||||
}
|
||||
grid := parseRendered(t, out)
|
||||
c, err := qr.Encode(qrTestURI, qr.M)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
const quiet = 4
|
||||
want := c.Size + 2*quiet
|
||||
|
||||
if len(grid) < want {
|
||||
t.Fatalf("rendered %d module rows, need at least %d (symbol %d + 2x%d quiet)",
|
||||
len(grid), want, c.Size, quiet)
|
||||
}
|
||||
if got := len(grid[0]); got != want {
|
||||
t.Fatalf("rendered %d module columns, want %d — quiet zone wrong, scanners will not find the symbol", got, want)
|
||||
}
|
||||
|
||||
// 1. Every module must survive the half-block packing unchanged. A single
|
||||
// inverted or shifted module is an unscannable code.
|
||||
for y := 0; y < c.Size; y++ {
|
||||
for x := 0; x < c.Size; x++ {
|
||||
if grid[y+quiet][x+quiet] != c.Black(x, y) {
|
||||
t.Fatalf("module (%d,%d) rendered %v, want %v — packing corrupts the symbol",
|
||||
x, y, grid[y+quiet][x+quiet], c.Black(x, y))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. The quiet zone must be genuinely clear on all four sides. This is the
|
||||
// single most common reason a hand-rolled terminal QR does not scan.
|
||||
for y := 0; y < want; y++ {
|
||||
for x := 0; x < want; x++ {
|
||||
inSymbol := x >= quiet && x < quiet+c.Size && y >= quiet && y < quiet+c.Size
|
||||
if !inSymbol && grid[y][x] {
|
||||
t.Fatalf("dark module at (%d,%d) inside the quiet zone", x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Orientation: the three finder patterns must be dark at their centres.
|
||||
// If this passes while the visual is inverted, the ANSI colours are wrong
|
||||
// — hence the explicit colour assertion below rather than only this.
|
||||
for _, p := range [][2]int{{3, 3}, {c.Size - 4, 3}, {3, c.Size - 4}} {
|
||||
if !c.Black(p[0], p[1]) {
|
||||
t.Fatalf("finder centre (%d,%d) is not dark — wrong orientation", p[0], p[1])
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Colours are set explicitly, never inherited. A dark-themed terminal
|
||||
// would otherwise render an inverted code that most apps refuse.
|
||||
if !strings.Contains(out, "\x1b[47;30m") {
|
||||
t.Fatal("renderer must force white-bg/black-fg — an inherited dark theme inverts the code")
|
||||
}
|
||||
if !strings.HasSuffix(strings.TrimRight(out, "\n"), "\x1b[0m") {
|
||||
t.Fatal("every row must reset SGR, or colour bleeds into the rest of the terminal")
|
||||
}
|
||||
}
|
||||
|
||||
// Writes the real rendered output for verification with an EXTERNAL decoder:
|
||||
//
|
||||
// QR_DUMP_URI='otpauth://…' go test -run TestQRDumpForExternalDecode
|
||||
// # then reconstruct the grid and: zbarimg --raw /tmp/qr.pbm
|
||||
//
|
||||
// Confirmed: zbarimg read back the exact URI, byte for byte.
|
||||
func TestQRDumpForExternalDecode(t *testing.T) {
|
||||
uri := os.Getenv("QR_DUMP_URI")
|
||||
if uri == "" {
|
||||
t.Skip("QR_DUMP_URI unset")
|
||||
}
|
||||
out, err := qrTerminal(uri)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := os.WriteFile("/tmp/qr.ansi", []byte(out), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user