pdftract/pdftract-swift/Tests/PdftractTests/ConformanceTests.swift
jedarden d0f52751ce fix(pdftract-39gey): fix indent trigger to not split drop-cap paragraphs
The indent trigger was using .abs() which fired on both increased indent
(non-indented → indented) AND decreased indent (indented → non-indented).
This caused drop-cap style paragraphs (indented first line, flush-left
continuation) to incorrectly split into two blocks.

Per plan Phase 4.4 heuristic #2, indent change should only trigger when the
current line is MORE indented (to the right, larger x0) than the block
average - i.e., a new paragraph starting after non-indented text. It should
NOT trigger for decreased indent (first line indented, rest flush-left).

Fix: Remove .abs() and only check if line_x0 - block_avg_x0 > threshold.

Tests:
- test_indented_first_line_new_block: PASS (non-indented → indented splits)
- test_indented_first_line_of_paragraph_not_split: PASS (drop cap stays together)
- All 179 line module tests: PASS
2026-06-07 13:43:19 -04:00

179 lines
6.7 KiB
Swift

//
// Conformance test suite for pdftract Swift SDK
// Auto-generated - do not edit manually
//
import XCTest
@testable import Pdftract
final class ConformanceTests: XCTestCase {
var client: Pdftract!
var suite: [String: Any]?
override func setUp() {
client = Pdftract()
let suitePath = ProcessInfo.processInfo.environment["CONFORMANCE_SUITE"] ?? "tests/sdk-conformance/cases.json"
if let data = try? Data(contentsOf: URL(fileURLWithPath: suitePath)),
let json = try? JSONSerialization.jsonObject(with: data) as? [String: Any] {
suite = json
}
}
func testBinaryAvailable() async throws {
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
process.arguments = ["sh", "-c", "pdftract --version"]
try process.run()
process.waitUntilExit()
XCTAssertEqual(process.terminationStatus, 0, "pdftract binary not found on PATH")
}
func testConformance() async throws {
guard let suite = suite,
let cases = suite["cases"] as? [[String: Any]] else {
throw XCTSkip("No conformance suite loaded")
}
for testCase in cases {
let id = testCase["id"] as? String ?? "unknown"
let method = testCase["method"] as? String ?? "unknown"
try await runTestCase(testCase, fixturePath: "fixtures/\(testCase["fixture"] as? String ?? "")")
}
}
private func runTestCase(_ testCase: [String: Any], fixturePath: String) async throws {
guard let method = testCase["method"] as? String else {
throw XCTSkip("No method specified")
}
switch method {
case "extract":
try await testExtract(fixturePath, assertions: testCase["assertions"] as? [String: Any])
case "extract_text":
try await testExtractText(fixturePath, assertions: testCase["assertions"] as? [String: Any])
case "extract_markdown":
try await testExtractMarkdown(fixturePath, assertions: testCase["assertions"] as? [String: Any])
case "get_metadata":
try await testGetMetadata(fixturePath, assertions: testCase["assertions"] as? [String: Any])
case "hash":
try await testHash(fixturePath, assertions: testCase["assertions"] as? [String: Any])
case "classify":
try await testClassify(fixturePath, assertions: testCase["assertions"] as? [String: Any])
case "verify_receipt":
try await testVerifyReceipt(fixturePath, assertions: testCase["assertions"] as? [String: Any])
case "search":
try await testSearch(fixturePath, assertions: testCase["assertions"] as? [String: Any])
case "extract_stream":
try await testExtractStream(fixturePath, assertions: testCase["assertions"] as? [String: Any])
default:
throw XCTSkip("Method not yet implemented: \(method)")
}
}
private func testExtract(_ fixturePath: String, assertions: [String: Any]?) async throws {
let doc = try await client.extract(.path(fixturePath))
if let pageCount = assertions?["page_count"] as? Int {
XCTAssertEqual(doc.pages.count, pageCount)
}
if let hasTitle = assertions?["has_title"] as? Bool, hasTitle {
XCTAssertNotNil(doc.metadata.title)
}
}
private func testExtractText(_ fixturePath: String, assertions: [String: Any]?) async throws {
let text = try await client.extractText(.path(fixturePath))
if let minLen = assertions?["min_length"] as? Int {
XCTAssertGreaterThanOrEqual(text.count, minLen)
}
if let contains = assertions?["contains"] as? [String] {
for substr in contains {
XCTAssertTrue(text.contains(substr), "Expected text to contain: \(substr)")
}
}
}
private func testExtractMarkdown(_ fixturePath: String, assertions: [String: Any]?) async throws {
let md = try await client.extractMarkdown(.path(fixturePath))
if let minLen = assertions?["min_length"] as? Int {
XCTAssertGreaterThanOrEqual(md.count, minLen)
}
}
private func testGetMetadata(_ fixturePath: String, assertions: [String: Any]?) async throws {
let metadata = try await client.getMetadata(.path(fixturePath))
if let pageCount = assertions?["page_count"] as? Int {
XCTAssertEqual(metadata.pageCount, pageCount)
}
}
private func testHash(_ fixturePath: String, assertions: [String: Any]?) async throws {
let fingerprint = try await client.hash(.path(fixturePath))
XCTAssertEqual(fingerprint.hash.count, 64)
XCTAssertEqual(fingerprint.fastHash.count, 64)
if let pageCount = assertions?["page_count"] as? Int {
XCTAssertEqual(fingerprint.pageCount, pageCount)
}
}
private func testClassify(_ fixturePath: String, assertions: [String: Any]?) async throws {
let classification = try await client.classify(.path(fixturePath))
XCTAssertFalse(classification.category.isEmpty)
XCTAssertTrue(classification.confidence >= 0 && classification.confidence <= 1)
}
private func testVerifyReceipt(_ fixturePath: String, assertions: [String: Any]?) async throws {
guard let receipt = assertions?["receipt"] as? String else {
throw XCTSkip("Receipt not provided in assertions")
}
let receiptStruct = Receipt(data: receipt)
let valid = try await client.verifyReceipt(fixturePath, receipt: receiptStruct)
if let expectedValid = assertions?["valid"] as? Bool {
XCTAssertEqual(valid, expectedValid)
}
}
private func testSearch(_ fixturePath: String, assertions: [String: Any]?) async throws {
guard let pattern = assertions?["pattern"] as? String else {
throw XCTSkip("Pattern not provided in assertions")
}
var matchCount = 0
for await _ in client.search(.path(fixturePath), pattern) {
matchCount += 1
if let maxResults = assertions?["max_results"] as? Int, matchCount >= maxResults {
break
}
}
if let minMatches = assertions?["min_matches"] as? Int {
XCTAssertGreaterThanOrEqual(matchCount, minMatches)
}
}
private func testExtractStream(_ fixturePath: String, assertions: [String: Any]?) async throws {
var pageCount = 0
for await _ in client.extractStream(.path(fixturePath)) {
pageCount += 1
}
if let expectedPages = assertions?["page_count"] as? Int {
XCTAssertEqual(pageCount, expectedPages)
}
}
}