Files

75 lines
2.6 KiB
Go

// Copyright (c) 2026 Storm Dragon
// Authors: Storm Dragon, Angus Kola
// SPDX-License-Identifier: GPL-3.0-or-later
package ocr
import (
"context"
"image"
"os"
"strings"
"testing"
"time"
)
// This is a real installed-engine check, intentionally opt-in. Ordinary tests
// and the reader build must work with no tesseract executable or language data.
func TestNativeTesseract(t *testing.T) {
if os.Getenv("CTHULHU_NATIVE_OCR") != "1" {
t.Skip("set CTHULHU_NATIVE_OCR=1 to test installed Tesseract")
}
engine := NewEngine()
catalog, err := engine.Discover(context.Background())
if err != nil {
t.Fatal(err)
}
if language, err := catalog.resolve_language("eng"); err != nil || language != "eng" {
t.Fatal("the native painted-text fixture requires installed English data")
}
file, err := os.Open("testdata/painted-text.png")
if err != nil {
t.Fatal(err)
}
defer file.Close()
pixels, _, err := image.Decode(file)
if err != nil {
t.Fatal(err)
}
for _, layout := range []Layout{AutomaticPage, SingleBlock, SparseText} {
start := time.Now()
result, err := engine.Recognize(context.Background(), pixels, Options{Language: "eng", Layout: layout})
if err != nil {
t.Fatal(err)
}
// Accept ordinary spacing/case variation between engine/model versions;
// this simple fixture is a pipe/coordinate proof, not an accuracy corpus.
actual := strings.ToLower(strings.Join(strings.Fields(result.Text), " "))
if actual != "ocr test window open settings value 42" {
t.Fatalf("PSM %d did not read the known fixture: %q", layout, result.Text)
}
if len(result.Lines) != 3 || len(result.Words) != 7 {
t.Fatalf("PSM %d lost line/word structure: %+v", layout, result)
}
for _, word := range result.Words {
if !valid_word_bounds(word.Bounds, result.Width, result.Height) ||
word.Bounds.Min.X < 20 || word.Bounds.Min.Y < 15 || word.Bounds.Max.Y > 185 {
t.Fatalf("PSM %d word outside painted area: %+v", layout, word)
}
}
t.Logf("PSM %d: %d words, %d lines, total %s (includes catalog and PNG encoding)", layout, len(result.Words), len(result.Lines), time.Since(start))
}
scaled, err := engine.Recognize(context.Background(), pixels, Options{Language: "eng", Layout: SingleBlock, Scale: 2, Grayscale: true})
if err != nil {
t.Fatal(err)
}
if scaled.Width != pixels.Bounds().Dx()*2 || scaled.Height != pixels.Bounds().Dy()*2 || len(scaled.Words) == 0 {
t.Fatalf("scaled recognition did not retain image coordinates: %dx%d, %d words", scaled.Width, scaled.Height, len(scaled.Words))
}
for _, word := range scaled.Words {
if !valid_word_bounds(word.Bounds, scaled.Width, scaled.Height) {
t.Fatalf("invalid scaled word: %+v", word)
}
}
}