package report import ( "bytes" "strings" "testing" ) // TestEscposQR_EmptyData 空数据应直接返回 nil,不写入任何指令 func TestEscposQR_EmptyData(t *testing.T) { var buf bytes.Buffer if err := escposQR(&buf, "", 6, 49); err != nil { t.Fatalf("空数据应返回 nil,得到 err=%v", err) } if buf.Len() != 0 { t.Errorf("空数据不应输出指令,实际写入 %d 字节", buf.Len()) } } // TestEscposQR_BasicCommands 验证基本 GS ( k 指令流 func TestEscposQR_BasicCommands(t *testing.T) { var buf bytes.Buffer data := "http://192.168.1.10:8080/reports/report-abc-10.0.0.1-20260824.html" if err := escposQR(&buf, data, 6, 49); err != nil { t.Fatalf("生成指令失败: %v", err) } got := buf.Bytes() // 必须包含 GS ( k 起始 if !bytes.Contains(got, []byte{0x1d, 0x28, 0x6b}) { t.Error("缺少 GS ( k 起始指令") } // 必须包含模型选择 cn=49 fn=65 n=50 if !bytes.Contains(got, []byte{0x31, 0x41, 0x32}) { t.Error("缺少 QR 模型选择指令 (31 41 32)") } // 必须包含模块大小 n=6 if !bytes.Contains(got, []byte{0x31, 0x43, 0x06}) { t.Error("缺少模块大小指令 (31 43 06)") } // 必须包含纠错级别 M (49) if !bytes.Contains(got, []byte{0x31, 0x45, 49}) { t.Error("缺少纠错级别指令 (31 45 49)") } // 必须包含数据存储 49 80 48 if !bytes.Contains(got, []byte{0x31, 0x50, 0x30}) { t.Error("缺少数据存储指令 (31 50 30)") } // 必须包含打印指令 49 81 48 if !bytes.Contains(got, []byte{0x31, 0x51, 0x30}) { t.Error("缺少打印指令 (31 51 30)") } // 数据本体 if !bytes.Contains(got, []byte(data)) { t.Error("缺少 URL 数据本体") } } // TestEscposQR_ClampSizeAndECC 验证 size/ecc 参数越界时自动夹紧 func TestEscposQR_ClampSizeAndECC(t *testing.T) { var buf1, buf2, buf3 bytes.Buffer // size 越界 0 → 夹紧为 1 _ = escposQR(&buf1, "abc", 0, 49) // size 越界 99 → 夹紧为 16 _ = escposQR(&buf2, "abc", 99, 49) // ecc 非法 99 → 夹紧为 49 (M) _ = escposQR(&buf3, "abc", 6, 99) if !bytes.Contains(buf1.Bytes(), []byte{0x31, 0x43, 0x01}) { t.Error("size=0 应夹紧为 1,未生效") } if !bytes.Contains(buf2.Bytes(), []byte{0x31, 0x43, 0x10}) { t.Error("size=99 应夹紧为 16,未生效") } if !bytes.Contains(buf3.Bytes(), []byte{0x31, 0x45, 49}) { t.Error("ecc=99 应夹紧为 49(M),未生效") } } // TestEscposQR_DataLengthEncoding pL/pH 编码正确(大数据场景) // 指令布局: 1d 28 6b pL pH 31 50 30 func TestEscposQR_DataLengthEncoding(t *testing.T) { var buf bytes.Buffer // 200 字节数据 → pL = 203 (0xCB), pH = 0 data := strings.Repeat("a", 200) _ = escposQR(&buf, data, 6, 49) got := buf.Bytes() wantPL := byte((200 + 3) & 0xff) // 203 = 0xCB wantPH := byte(((200 + 3) >> 8) & 0xff) // 数据存储标记 "31 50 30" 在 got 中的位置 dataMarker := []byte{0x31, 0x50, 0x30} idx := bytes.Index(got, dataMarker) if idx < 0 { t.Fatal("未找到数据存储指令 31 50 30") } // 期望布局:1d(GS) 28 ( 6b k pL pH 31 50 30 ... // got[idx-5] = 0x1d, got[idx-4] = 0x28, got[idx-3] = 0x6b // got[idx-2] = pL, got[idx-1] = pH if got[idx-5] != 0x1d || got[idx-4] != 0x28 || got[idx-3] != 0x6b { t.Fatalf("pL/pH 前不是 GS ( k,实际前 5 字节 = % x", got[idx-5:idx]) } if got[idx-2] != wantPL || got[idx-1] != wantPH { t.Errorf("pL/pH 编码错误: got=0x%02x 0x%02x, want=0x%02x 0x%02x", got[idx-2], got[idx-1], wantPL, wantPH) } } // TestSystemPrinter_PrintQR 系统打印后端 no-op func TestSystemPrinter_PrintQR(t *testing.T) { p := &SystemPrinter{} if err := p.PrintQR("http://example.com"); err != nil { t.Errorf("SystemPrinter.PrintQR 应返回 nil,得到 %v", err) } } // TestFilePrinter_PrintQR 文件后端写入提示行 + URL func TestFilePrinter_PrintQR(t *testing.T) { tmp := t.TempDir() p, err := NewFilePrinter(tmp + "/out.log") if err != nil { t.Fatalf("创建 FilePrinter 失败: %v", err) } defer p.Close() url := "http://192.168.1.10/reports/r.html" if err := p.PrintQR(url); err != nil { t.Fatalf("PrintQR 失败: %v", err) } }