mirror of
https://github.com/infiniflow/ragflow.git
synced 2026-07-07 20:10:14 +08:00
### What problem does this PR solve? Package refactor and PDF post process. ### Type of change - [x] Refactoring --------- Co-authored-by: Claude <noreply@anthropic.com>
236 lines
7.4 KiB
Go
236 lines
7.4 KiB
Go
package table
|
|
|
|
import (
|
|
"testing"
|
|
|
|
pdf "ragflow/internal/deepdoc/parser/pdf/type"
|
|
)
|
|
|
|
func cellTexts(cells []pdf.TSRCell) []string {
|
|
out := make([]string, len(cells))
|
|
for i, c := range cells {
|
|
out[i] = c.Text
|
|
}
|
|
return out
|
|
}
|
|
|
|
func TestGroupTSRCellsToRows(t *testing.T) {
|
|
t.Run("empty", func(t *testing.T) {
|
|
if rows := GroupTSRCellsToRows(nil); rows != nil {
|
|
t.Error("nil → nil")
|
|
}
|
|
if rows := GroupTSRCellsToRows([]pdf.TSRCell{}); rows != nil {
|
|
t.Error("empty → nil")
|
|
}
|
|
})
|
|
|
|
t.Run("single cell", func(t *testing.T) {
|
|
cells := []pdf.TSRCell{{X0: 0, Y0: 0, X1: 100, Y1: 50, Text: "A"}}
|
|
rows := GroupTSRCellsToRows(cells)
|
|
if len(rows) != 1 || rows[0][0].Text != "A" {
|
|
t.Error("single cell not preserved")
|
|
}
|
|
})
|
|
|
|
t.Run("two rows two cols", func(t *testing.T) {
|
|
cells := []pdf.TSRCell{
|
|
{X0: 0, Y0: 0, X1: 50, Y1: 30, Text: "A"},
|
|
{X0: 50, Y0: 0, X1: 100, Y1: 30, Text: "B"},
|
|
{X0: 0, Y0: 50, X1: 50, Y1: 80, Text: "C"},
|
|
{X0: 50, Y0: 50, X1: 100, Y1: 80, Text: "D"},
|
|
}
|
|
rows := GroupTSRCellsToRows(cells)
|
|
if len(rows) != 2 {
|
|
t.Fatalf("2 rows expected, got %d", len(rows))
|
|
}
|
|
if rows[0][0].Text != "A" || rows[0][1].Text != "B" {
|
|
t.Errorf("row0: %v", cellTexts(rows[0]))
|
|
}
|
|
if rows[1][0].Text != "C" || rows[1][1].Text != "D" {
|
|
t.Errorf("row1: %v", cellTexts(rows[1]))
|
|
}
|
|
})
|
|
|
|
t.Run("unsorted input", func(t *testing.T) {
|
|
cells := []pdf.TSRCell{
|
|
{X0: 50, Y0: 50, X1: 100, Y1: 80, Text: "D"},
|
|
{X0: 0, Y0: 0, X1: 50, Y1: 30, Text: "A"},
|
|
{X0: 0, Y0: 50, X1: 50, Y1: 80, Text: "C"},
|
|
{X0: 50, Y0: 0, X1: 100, Y1: 30, Text: "B"},
|
|
}
|
|
rows := GroupTSRCellsToRows(cells)
|
|
if len(rows) != 2 {
|
|
t.Fatalf("unsorted: 2 rows expected, got %d", len(rows))
|
|
}
|
|
if rows[0][0].Text != "A" || rows[0][1].Text != "B" {
|
|
t.Errorf("unsorted row0: %v", cellTexts(rows[0]))
|
|
}
|
|
})
|
|
|
|
t.Run("tall merged cell", func(t *testing.T) {
|
|
cells := []pdf.TSRCell{
|
|
{X0: 0, Y0: 0, X1: 50, Y1: 100, Text: "merged"},
|
|
{X0: 50, Y0: 0, X1: 100, Y1: 30, Text: "B"},
|
|
{X0: 50, Y0: 50, X1: 100, Y1: 80, Text: "D"},
|
|
}
|
|
rows := GroupTSRCellsToRows(cells)
|
|
// merged cell starts Y0=0 → row 0; Y0=50 cell → row 1
|
|
if len(rows) != 2 {
|
|
t.Fatalf("merged cell: 2 rows expected, got %d", len(rows))
|
|
}
|
|
})
|
|
|
|
t.Run("large gap different rows", func(t *testing.T) {
|
|
cells := []pdf.TSRCell{
|
|
{X0: 0, Y0: 0, X1: 50, Y1: 30, Text: "top"},
|
|
{X0: 0, Y0: 200, X1: 50, Y1: 230, Text: "far"},
|
|
}
|
|
rows := GroupTSRCellsToRows(cells)
|
|
if len(rows) != 2 {
|
|
t.Fatalf("large gap: 2 rows expected, got %d", len(rows))
|
|
}
|
|
})
|
|
|
|
t.Run("close rows", func(t *testing.T) {
|
|
cells := []pdf.TSRCell{
|
|
{X0: 0, Y0: 0, X1: 10, Y1: 8, Text: "Row1"},
|
|
{X0: 0, Y0: 9, X1: 10, Y1: 17, Text: "Row2"},
|
|
}
|
|
rows := GroupTSRCellsToRows(cells)
|
|
if len(rows) != 2 {
|
|
t.Errorf("close rows: expected 2, got %d", len(rows))
|
|
}
|
|
})
|
|
|
|
t.Run("varying heights", func(t *testing.T) {
|
|
cells := []pdf.TSRCell{
|
|
{X0: 0, Y0: 0, X1: 10, Y1: 5, Text: "A"},
|
|
{X0: 0, Y0: 50, X1: 10, Y1: 70, Text: "B"},
|
|
{X0: 0, Y0: 50, X1: 10, Y1: 70, Text: "C"},
|
|
}
|
|
rows := GroupTSRCellsToRows(cells)
|
|
if len(rows) != 2 {
|
|
t.Fatalf("varying heights: expected 2 rows, got %d", len(rows))
|
|
}
|
|
if len(rows[0]) != 1 || rows[0][0].Text != "A" {
|
|
t.Errorf("row 0: expected [A], got %v", cellTexts(rows[0]))
|
|
}
|
|
})
|
|
}
|
|
|
|
// ── fillCellTextFromBoxes ──────────────────────────────────────────────
|
|
|
|
func TestFillCellTextFromBoxes(t *testing.T) {
|
|
t.Run("exact match", func(t *testing.T) {
|
|
cells := []pdf.TSRCell{
|
|
{X0: 0, Y0: 0, X1: 100, Y1: 50},
|
|
{X0: 100, Y0: 0, X1: 200, Y1: 50},
|
|
}
|
|
boxes := []pdf.TextBox{
|
|
{X0: 0, X1: 100, Top: 0, Bottom: 50, Text: "A"},
|
|
{X0: 100, X1: 200, Top: 0, Bottom: 50, Text: "B"},
|
|
}
|
|
FillCellTextFromBoxes(cells, boxes)
|
|
if cells[0].Text != "A" || cells[1].Text != "B" {
|
|
t.Errorf("got %q/%q, want A/B", cells[0].Text, cells[1].Text)
|
|
}
|
|
})
|
|
|
|
t.Run("empty cells", func(t *testing.T) {
|
|
cells := []pdf.TSRCell{
|
|
{X0: 0, Y0: 0, X1: 100, Y1: 50},
|
|
{X0: 100, Y0: 0, X1: 200, Y1: 50},
|
|
}
|
|
boxes := []pdf.TextBox{
|
|
{X0: 0, X1: 100, Top: 0, Bottom: 50, Text: "only first"},
|
|
}
|
|
FillCellTextFromBoxes(cells, boxes)
|
|
if cells[0].Text != "only first" {
|
|
t.Errorf("cell[0]: got %q", cells[0].Text)
|
|
}
|
|
if cells[1].Text != "" {
|
|
t.Errorf("cell[1] should be empty, got %q", cells[1].Text)
|
|
}
|
|
})
|
|
|
|
t.Run("partial cell coverage — empty cell filled from any overlapping box", func(t *testing.T) {
|
|
// Box covers 40% of cell area. Old code rejected (<85% cell coverage).
|
|
// New code: cell is empty → accepts box (≥30% box area inside cell).
|
|
cells := []pdf.TSRCell{{X0: 0, Y0: 0, X1: 200, Y1: 50}}
|
|
boxes := []pdf.TextBox{{X0: 0, X1: 80, Top: 0, Bottom: 50, Text: "partial"}}
|
|
FillCellTextFromBoxes(cells, boxes)
|
|
if cells[0].Text != "partial" {
|
|
t.Errorf("empty cell should be filled from overlapping box, got %q", cells[0].Text)
|
|
}
|
|
})
|
|
|
|
t.Run("box inside cell >85%", func(t *testing.T) {
|
|
cells := []pdf.TSRCell{{X0: 0, Y0: 0, X1: 500, Y1: 300}}
|
|
boxes := []pdf.TextBox{{X0: 10, X1: 490, Top: 10, Bottom: 290, Text: "inside"}}
|
|
FillCellTextFromBoxes(cells, boxes)
|
|
if cells[0].Text != "inside" {
|
|
t.Errorf("got %q", cells[0].Text)
|
|
}
|
|
})
|
|
|
|
t.Run("concatenate two boxes to same cell", func(t *testing.T) {
|
|
cells := []pdf.TSRCell{{X0: 0, Y0: 0, X1: 200, Y1: 100}}
|
|
boxes := []pdf.TextBox{
|
|
{X0: 5, X1: 195, Top: 2, Bottom: 98, Text: "hello"},
|
|
{X0: 5, X1: 195, Top: 2, Bottom: 98, Text: "world"},
|
|
}
|
|
FillCellTextFromBoxes(cells, boxes)
|
|
if cells[0].Text != "hello world" {
|
|
t.Errorf("got %q, want 'hello world'", cells[0].Text)
|
|
}
|
|
})
|
|
|
|
t.Run("empty inputs", func(t *testing.T) {
|
|
FillCellTextFromBoxes(nil, nil)
|
|
FillCellTextFromBoxes([]pdf.TSRCell{}, []pdf.TextBox{})
|
|
c := []pdf.TSRCell{{X0: 0, Y0: 0, X1: 1, Y1: 1}}
|
|
FillCellTextFromBoxes(c, nil)
|
|
if c[0].Text != "" {
|
|
t.Error("no boxes → text empty")
|
|
}
|
|
})
|
|
}
|
|
|
|
// ── enrichWithDeepDoc noop ─────────────────────────────────────────────
|
|
|
|
func TestGroupTSRCellsToRows_SameHeight(t *testing.T) {
|
|
// All cells have identical height → medianH is that value → threshold = medianH/2
|
|
cells := []pdf.TSRCell{
|
|
{X0: 0, Y0: 0, X1: 50, Y1: 30, Text: "A"},
|
|
{X0: 50, Y0: 0, X1: 100, Y1: 30, Text: "B"},
|
|
{X0: 0, Y0: 31, X1: 50, Y1: 61, Text: "C"}, // gap = 31-30=1 < 30/2=15 → same row? NO, Y0=31 is right at edge
|
|
}
|
|
rows := GroupTSRCellsToRows(cells)
|
|
// medianH=30, threshold=15. C.Y0=31 > curY+threshold?" curY=0, 31 > 15 → new row.
|
|
// So A,B in row 0, C in row 1.
|
|
if len(rows) != 2 {
|
|
t.Fatalf("expected 2 rows, got %d", len(rows))
|
|
}
|
|
if len(rows[0]) != 2 || len(rows[1]) != 1 {
|
|
t.Errorf("row sizes: %d %d, want 2 1", len(rows[0]), len(rows[1]))
|
|
}
|
|
}
|
|
|
|
func TestFillCellTextFromBoxes_WhitespaceTrim(t *testing.T) {
|
|
cells := []pdf.TSRCell{{X0: 0, Y0: 0, X1: 100, Y1: 100}}
|
|
boxes := []pdf.TextBox{{X0: 0, X1: 100, Top: 0, Bottom: 100, Text: " hello "}}
|
|
FillCellTextFromBoxes(cells, boxes)
|
|
if cells[0].Text != "hello" {
|
|
t.Errorf("got %q, want 'hello'", cells[0].Text)
|
|
}
|
|
}
|
|
|
|
func TestFillCellTextFromBoxes_EmptyBoxIgnored(t *testing.T) {
|
|
cells := []pdf.TSRCell{{X0: 0, Y0: 0, X1: 100, Y1: 100}}
|
|
boxes := []pdf.TextBox{{X0: 0, X1: 100, Top: 0, Bottom: 100, Text: " "}} // all whitespace
|
|
FillCellTextFromBoxes(cells, boxes)
|
|
if cells[0].Text != "" {
|
|
t.Errorf("whitespace text should produce empty, got %q", cells[0].Text)
|
|
}
|
|
}
|