seedhammer-v1-companion/engrave/engrave_test.go
mineracks 9261cf368a Lift composer substrate from upstream v1.3.0
Twelve packages lifted from seedhammer/seedhammer @ v1.3.0
(commit 2f071c1d8f23eb7fd39b15fc0acb8874113f801e):

  address/     — bitcoin address parsing
  backup/      — v1 plate dimensions + UR-coded multi-plate backup
  bc/          — Blockchain Commons: ur, fountain, bytewords, urtypes,
                 xoshiro256 (5 subpkgs)
  bip32/       — HD-key derivation
  bip39/       — mnemonic seed phrases + 2048-word wordlist
  engrave/     — text/QR → MoveTo/LineTo command stream conversion
  seedqr/      — SeedQR / CompactSeedQR encoders
  image/       — paletted, rgb565, alpha4, ninepatch image formats
  nonstandard/ — bitcoin descriptor + script parsing
  font/        — bitmap + vector font runtime
  font/{comfortaa,poppins,constant,bitmap,vector}/  — actual fonts
  driver/mjolnir/  — MarkingWay USB-serial engraver driver

Plus an earlier-aside backup_test.go restored (its deps are now lifted).

Import paths globally rewritten seedhammer.com → mineracks namespace
via single sed pass; verified no orphan refs remain. go.mod adopts
upstream's full dep set plus the replace-directive for the patched
kortschak/qr fork.

  go build ./...     clean (all 27 packages)
  go test ./...      clean (12 packages with tests, all passing)

NOT lifted in this commit:
  - driver/{wshat,drm,libcamera}  (hardware-specific GPIO/LCD/camera —
    will be platform-v1/-shaped abstractions instead)
  - gui/                (depends on the above; lifts in Phase 2)
  - cmd/{controller,...} (Pi binary entrypoints — not needed for the
    companion repo)
  - zbar/               (QR scanner — needs libcamera)

Next:
  - Write the SH1E reference encoder/decoder in engrave/wire/sh1e/
  - Lift Gangleri42's cmd/webnfc/ shell + retune to v1 plates
  - First buildable composer WASM with a working preview

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 18:36:40 +10:00

100 lines
2.2 KiB
Go

package engrave
import (
"image"
"io"
"math/rand"
"reflect"
"strings"
"testing"
"github.com/kortschak/qr"
"github.com/mineracks/seedhammer-v1-companion/bip39"
"github.com/mineracks/seedhammer-v1-companion/font/constant"
)
func TestConstantQR(t *testing.T) {
rng := rand.New(rand.NewSource(44))
for i := 0; i < 100; i++ {
for n := 16; n <= 32; n++ {
entropy := make([]byte, n)
if _, err := io.ReadFull(rng, entropy); err != nil {
t.Fatal(err)
}
lvl := qr.Q
cmd, err := constantQR(7, 4, lvl, entropy)
if err != nil {
t.Fatalf("entropy: %x: %v", entropy, err)
}
qrc, err := qr.Encode(string(entropy), lvl)
if err != nil {
t.Fatal(err)
}
dim := qrc.Size
want := bitmapForQR(qrc)
_, _, got := bitmapForQRStatic(dim)
for _, p := range cmd.plan {
got.Set(p)
}
if !reflect.DeepEqual(got, want) {
t.Fatalf("entropy: %x: engraving plan doesn't match QR code", entropy)
}
}
}
}
func TestConstantString(t *testing.T) {
s := NewConstantStringer(constant.Font, 1000, bip39.ShortestWord, bip39.LongestWord)
for i := bip39.Word(0); i < bip39.NumWords; i++ {
w := strings.ToUpper(bip39.LabelFor(i))
cmd := s.String(w)
bounds := image.Rect(0, 0, s.longest*s.dims.X, s.dims.Y)
moves := measureMoves(cmd)
if !moves.In(bounds) {
t.Errorf("%s movement bounds %v are not inside bounds %v", w, moves, bounds)
}
}
}
func FuzzConstantQR(f *testing.F) {
f.Fuzz(func(t *testing.T, entropy []byte) {
if len(entropy) < 16 {
return
}
if len(entropy) > 32 {
entropy = entropy[:32]
}
if _, err := ConstantQR(1, 3, qr.Q, entropy); err != nil {
t.Fatalf("entropy: %x: %v", entropy, err)
}
if _, err := ConstantQR(1, 3, qr.L, entropy); err != nil {
t.Fatalf("entropy: %x: %v", entropy, err)
}
})
}
func measureMoves(p Plan) image.Rectangle {
inf := image.Rectangle{Min: image.Pt(1e6, 1e6), Max: image.Pt(-1e6, -1e6)}
bounds := inf
p(func(cmd Command) {
if cmd.Line {
return
}
p := cmd.Coord
if p.X < bounds.Min.X {
bounds.Min.X = p.X
} else if p.X > bounds.Max.X {
bounds.Max.X = p.X
}
if p.Y < bounds.Min.Y {
bounds.Min.Y = p.Y
} else if p.Y > bounds.Max.Y {
bounds.Max.Y = p.Y
}
})
if bounds == inf {
bounds = image.Rectangle{}
}
return bounds
}