Initial commit
Test / test (push) Successful in 7m5s
Release / gates (push) Successful in 7m28s
Release / build (amd64, freebsd) (push) Successful in 2m52s
Release / build (amd64, linux) (push) Successful in 2m46s
Release / build (arm64, freebsd) (push) Successful in 2m22s
Release / build (arm64, linux) (push) Successful in 2m38s
Release / build (loong64, linux) (push) Successful in 2m7s
Release / build (riscv64, linux) (push) Successful in 2m17s
Release / release (push) Successful in 1m0s
Test / test (push) Successful in 7m5s
Release / gates (push) Successful in 7m28s
Release / build (amd64, freebsd) (push) Successful in 2m52s
Release / build (amd64, linux) (push) Successful in 2m46s
Release / build (arm64, freebsd) (push) Successful in 2m22s
Release / build (arm64, linux) (push) Successful in 2m38s
Release / build (loong64, linux) (push) Successful in 2m7s
Release / build (riscv64, linux) (push) Successful in 2m17s
Release / release (push) Successful in 1m0s
Assisted-by: GLM 5.3
This commit is contained in:
@@ -0,0 +1,144 @@
|
||||
// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
|
||||
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
|
||||
|
||||
package qrcode
|
||||
|
||||
import (
|
||||
"os/exec"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// reference renders the same symbol with the system qrencode and
|
||||
// returns its module matrix, or skips the test when the tool is absent.
|
||||
func reference(t *testing.T, text string) [][]bool {
|
||||
t.Helper()
|
||||
binary, err := exec.LookPath("qrencode")
|
||||
if err != nil {
|
||||
t.Skip("qrencode is not installed; the differential test cannot run")
|
||||
}
|
||||
out, err := exec.Command(binary, "-l", "M", "-m", "0", "--type=ASCII", text).Output()
|
||||
if err != nil {
|
||||
t.Fatalf("qrencode: %v", err)
|
||||
}
|
||||
var rows [][]bool
|
||||
for line := range strings.SplitSeq(string(out), "\n") {
|
||||
line = strings.TrimSuffix(line, "\r")
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
// A row may be entirely light modules, which trims to spaces:
|
||||
// only a truly empty line separates rows, so nothing is dropped.
|
||||
if len(line)%2 != 0 {
|
||||
t.Fatalf("odd ASCII line %q", line)
|
||||
}
|
||||
row := make([]bool, len(line)/2)
|
||||
for i := range row {
|
||||
row[i] = line[i*2] == '#'
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
return rows
|
||||
}
|
||||
|
||||
// referenceMask decodes the mask the reference chose from its own
|
||||
// format information, using the layout this package writes.
|
||||
func referenceMask(t *testing.T, ref [][]bool) int {
|
||||
t.Helper()
|
||||
var word uint
|
||||
cells := func(y, x int) uint {
|
||||
if ref[y][x] {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
for i := range 6 {
|
||||
word = word<<1 | cells(8, i)
|
||||
}
|
||||
word = word<<1 | cells(8, 7)
|
||||
word = word<<1 | cells(8, 8)
|
||||
word = word<<1 | cells(7, 8)
|
||||
for i := range 6 {
|
||||
word = word<<1 | cells(5-i, 8)
|
||||
}
|
||||
// The stored word carries the XOR mask; the data sits on top once
|
||||
// it is undone.
|
||||
return int((word^0x5412)>>10) & 7
|
||||
}
|
||||
|
||||
func diffMatrices(t *testing.T, text string) {
|
||||
t.Helper()
|
||||
// The reference's own mask, forced: every other bit of the symbol
|
||||
// must then match, which proves the encoding, the error correction,
|
||||
// the placement and the format information. The mask each encoder
|
||||
// picks for itself is a choice among equally valid symbols.
|
||||
theirs := reference(t, text)
|
||||
mask := referenceMask(t, theirs)
|
||||
ours, err := encodeMasked([]byte(text), mask)
|
||||
if err != nil {
|
||||
t.Fatalf("encode %q: %v", text, err)
|
||||
}
|
||||
if len(theirs) != len(ours) {
|
||||
t.Fatalf("%q: size %d, reference %d", text, len(ours), len(theirs))
|
||||
}
|
||||
for y := range ours {
|
||||
for x := range ours[y] {
|
||||
if ours[y][x] != theirs[y][x] {
|
||||
t.Fatalf("%q: module differs at (%d,%d)", text, x, y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestAgainstQrencode is the proof of the encoder: for the full range
|
||||
// of payloads, every module must match the reference implementation
|
||||
// once its own mask is forced. The payloads stay clear of the
|
||||
// reference's segmentation optimiser: letters and underscores carry no
|
||||
// alphanumeric runs worth switching for, so it speaks plain byte mode
|
||||
// the way this package always does.
|
||||
func TestAgainstQrencode(t *testing.T) {
|
||||
payloads := []string{
|
||||
"hello world",
|
||||
"otpauth_totp_volumen_petr_secret_and_issuer_example_uri",
|
||||
"https_petrbalvin_org_neco_delsi_cesta_query_hodnota_druha_ano",
|
||||
strings.Repeat("a", 14), // v1 edge
|
||||
strings.Repeat("a", 15), // v2
|
||||
strings.Repeat("b", 84), // v5 edge
|
||||
strings.Repeat("b", 85), // v6
|
||||
strings.Repeat("c", 180), // v9 edge
|
||||
strings.Repeat("c", 181), // v10 (16-bit count)
|
||||
strings.Repeat("d", 450), // v16 edge
|
||||
}
|
||||
for i := range 10 {
|
||||
payloads = append(payloads, strings.Repeat("x", 1+i*45))
|
||||
}
|
||||
for _, p := range payloads {
|
||||
diffMatrices(t, p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSVGShape(t *testing.T) {
|
||||
svg, err := SVG("https://example.com/")
|
||||
if err != nil {
|
||||
t.Fatalf("SVG: %v", err)
|
||||
}
|
||||
for _, want := range []string{
|
||||
`viewBox="0 0 33 33"`, // v1 + quiet zone
|
||||
`<rect width="100%" height="100%" fill="#fff"/>`,
|
||||
`fill="#000"`,
|
||||
`</svg>`,
|
||||
} {
|
||||
if !strings.Contains(svg, want) {
|
||||
t.Fatalf("missing %q in %s", want, svg[:min(len(svg), 200)])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTooLongIsRefused(t *testing.T) {
|
||||
if _, err := SVG(strings.Repeat("z", 450)); err != nil {
|
||||
t.Fatalf("version 16 capacity refused: %v", err)
|
||||
}
|
||||
if _, err := SVG(strings.Repeat("z", 451)); err == nil {
|
||||
t.Fatal("input beyond version 16 accepted")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user