initial commit
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
package escpos
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"image/png"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
)
|
||||
|
||||
type recorder struct {
|
||||
mu sync.Mutex
|
||||
buf []byte
|
||||
}
|
||||
|
||||
func (r *recorder) reply(b []byte) {
|
||||
r.mu.Lock()
|
||||
r.buf = append(r.buf, b...)
|
||||
r.mu.Unlock()
|
||||
}
|
||||
|
||||
func (r *recorder) take() []byte {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
b := r.buf
|
||||
r.buf = nil
|
||||
return b
|
||||
}
|
||||
|
||||
func newTestPrinter(t *testing.T, key string) *Printer {
|
||||
t.Helper()
|
||||
prof, ok := FindProfile(key)
|
||||
if !ok {
|
||||
t.Fatalf("perfil %q no existe", key)
|
||||
}
|
||||
p, err := NewPrinter(prof)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return p
|
||||
}
|
||||
|
||||
func blackPixels(p *Printer) int {
|
||||
n := 0
|
||||
for _, t := range p.Tickets() {
|
||||
for _, v := range t.Image.Pix {
|
||||
if v < 0x80 {
|
||||
n++
|
||||
}
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
func TestTextAndCut(t *testing.T) {
|
||||
p := newTestPrinter(t, "80mm")
|
||||
s := p.NewSession("test", nil)
|
||||
s.Write([]byte("\x1b@Hola mundo\n\x1dVA\x00"))
|
||||
if got := len(p.Tickets()); got != 1 {
|
||||
t.Fatalf("tickets = %d, want 1", got)
|
||||
}
|
||||
if blackPixels(p) == 0 {
|
||||
t.Fatal("el ticket está en blanco")
|
||||
}
|
||||
}
|
||||
|
||||
func TestCutWithoutFeedLeavesTail(t *testing.T) {
|
||||
p := newTestPrinter(t, "80mm")
|
||||
s := p.NewSession("test", nil)
|
||||
s.Write([]byte("linea\n\x1dV\x00"))
|
||||
if got := len(p.Tickets()); got != 0 {
|
||||
t.Fatalf("con una sola lÃnea bajo la cuchilla no debe salir ticket, salen %d", got)
|
||||
}
|
||||
s.Write([]byte("\x1dVA\x00"))
|
||||
if got := len(p.Tickets()); got != 1 {
|
||||
t.Fatalf("tickets = %d, want 1", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSplitAcrossWrites(t *testing.T) {
|
||||
p := newTestPrinter(t, "80mm")
|
||||
s := p.NewSession("test", nil)
|
||||
img := []byte{0x1d, 'v', '0', 0, 2, 0, 3, 0, 0xff, 0xff, 0x81, 0x81, 0xff, 0xff}
|
||||
for _, c := range img {
|
||||
s.Write([]byte{c})
|
||||
}
|
||||
s.Write([]byte("\x1dVA\x00"))
|
||||
if blackPixels(p) != 16+4+16 {
|
||||
t.Fatalf("pÃxeles negros = %d, want 36", blackPixels(p))
|
||||
}
|
||||
}
|
||||
|
||||
func TestRealtimeStatusWhileCoverOpen(t *testing.T) {
|
||||
p := newTestPrinter(t, "80mm")
|
||||
var r recorder
|
||||
s := p.NewSession("test", r.reply)
|
||||
|
||||
s.Write([]byte{dle, 0x04, 1})
|
||||
if got := r.take(); !bytes.Equal(got, []byte{0x12}) {
|
||||
t.Fatalf("DLE EOT 1 online = % X, want 12", got)
|
||||
}
|
||||
|
||||
p.UpdateStatus(func(st *Status) { st.CoverOpen = true })
|
||||
s.Write([]byte("retenido\n"))
|
||||
s.Write([]byte{dle, 0x04, 2})
|
||||
if got := r.take(); !bytes.Equal(got, []byte{0x12 | 0x04 | 0x40}) {
|
||||
t.Fatalf("DLE EOT 2 con tapa abierta = % X, want 56", got)
|
||||
}
|
||||
if p.Snapshot(0).PendingBytes == 0 {
|
||||
t.Fatal("los datos deberÃan seguir en el buffer")
|
||||
}
|
||||
|
||||
p.UpdateStatus(func(st *Status) { st.CoverOpen = false })
|
||||
if p.Snapshot(0).PendingBytes != 0 {
|
||||
t.Fatal("al cerrar la tapa se deberÃa imprimir el buffer")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGSI(t *testing.T) {
|
||||
p := newTestPrinter(t, "80mm")
|
||||
var r recorder
|
||||
s := p.NewSession("test", r.reply)
|
||||
s.Write([]byte{gs, 'I', 67})
|
||||
if got, want := r.take(), []byte("_ESCPOS-EMU-80\x00"); !bytes.Equal(got, want) {
|
||||
t.Fatalf("GS I 67 = %q, want %q", got, want)
|
||||
}
|
||||
|
||||
p58 := newTestPrinter(t, "58mm")
|
||||
s58 := p58.NewSession("test", r.reply)
|
||||
s58.Write([]byte{gs, 'I', 67})
|
||||
if got := r.take(); len(got) != 0 {
|
||||
t.Fatalf("el perfil 58mm no deberÃa responder a GS I 67, responde %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQRAndDrawer(t *testing.T) {
|
||||
p := newTestPrinter(t, "80mm")
|
||||
var r recorder
|
||||
s := p.NewSession("test", r.reply)
|
||||
data := "https://example.com"
|
||||
n := len(data) + 3
|
||||
cmd := []byte{gs, '(', 'k', byte(n), byte(n >> 8), 49, 80, 48}
|
||||
cmd = append(cmd, data...)
|
||||
cmd = append(cmd, gs, '(', 'k', 3, 0, 49, 67, 4)
|
||||
cmd = append(cmd, gs, '(', 'k', 3, 0, 49, 81, 48)
|
||||
cmd = append(cmd, esc, 'p', 0, 25, 250)
|
||||
cmd = append(cmd, dle, 0x04, 1)
|
||||
cmd = append(cmd, gs, 'V', 'A', 0)
|
||||
s.Write(cmd)
|
||||
if blackPixels(p) < 500 {
|
||||
t.Fatalf("el QR no parece impreso (%d pÃxeles negros)", blackPixels(p))
|
||||
}
|
||||
if got := r.take(); !bytes.Equal(got, []byte{0x16}) {
|
||||
t.Fatalf("DLE EOT 1 con cajón abierto = % X, want 16", got)
|
||||
}
|
||||
}
|
||||
|
||||
// TestSampleReceipt genera un ticket de ejemplo. Con ESCPOS_PNG=dir se
|
||||
// guarda como PNG para inspeccionarlo a ojo.
|
||||
func TestSampleReceipt(t *testing.T) {
|
||||
p := newTestPrinter(t, "80mm")
|
||||
s := p.NewSession("test", nil)
|
||||
var b bytes.Buffer
|
||||
b.WriteString("\x1b@\x1bt\x13\x1ba\x01\x1d!\x11TIENDA\n\x1d!\x00")
|
||||
b.WriteString("\x1bE\x01Negrita\x1bE\x00 normal \x1b-\x01subrayado\x1b-\x00\n")
|
||||
b.WriteString("\x1ba\x00Ca\xa4a 1,50 \xd5\n") // PC858: ñ y €
|
||||
b.WriteString("\x1dB\x01 INVERTIDO \x1dB\x00\x1bM\x01 fuente B\x1bM\x00\n")
|
||||
b.WriteString("\x1ba\x02Total: 12,00\n\x1ba\x00")
|
||||
b.WriteString("Col1\tCol2\tCol3\n")
|
||||
b.WriteString("\x1dVB\x10")
|
||||
s.Write(b.Bytes())
|
||||
|
||||
tix := p.Tickets()
|
||||
if len(tix) != 1 {
|
||||
t.Fatalf("tickets = %d, want 1", len(tix))
|
||||
}
|
||||
if dir := os.Getenv("ESCPOS_PNG"); dir != "" {
|
||||
f, err := os.Create(filepath.Join(dir, "sample.png"))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer f.Close()
|
||||
if err := png.Encode(f, tix[0].Image); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user