917eedffcf
Main / Python 3.11 - Docs (push) Waiting to run
Main / Python 3.11 - Build (push) Waiting to run
Main / Python 3.11 - Lint (push) Waiting to run
Main / Python 3.11 - Style (push) Waiting to run
Main / Python 3.11 - Test (push) Waiting to run
Main / GPU CI (push) Blocked by required conditions
Main / Release (push) Blocked by required conditions
Main / Build and Push Docker Images (push) Blocked by required conditions
2355 lines
100 KiB
Python
2355 lines
100 KiB
Python
import unittest
|
||
|
||
from olmocr.bench.tests import (
|
||
BaselineTest,
|
||
BasePDFTest,
|
||
FootnoteTest,
|
||
FormatTest,
|
||
MathTest,
|
||
TableTest,
|
||
TestChecked,
|
||
TestType,
|
||
TextOrderTest,
|
||
TextPresenceTest,
|
||
ValidationError,
|
||
normalize_text,
|
||
parse_html_tables,
|
||
parse_markdown_tables,
|
||
)
|
||
|
||
|
||
class TestNormalizeText(unittest.TestCase):
|
||
"""Test the normalize_text function"""
|
||
|
||
def test_whitespace_normalization(self):
|
||
"""Test that whitespace is properly normalized"""
|
||
input_text = "This has\tmultiple spaces\nand\nnewlines"
|
||
expected = "This has multiple spaces and newlines"
|
||
self.assertEqual(normalize_text(input_text), expected)
|
||
|
||
def test_character_replacement(self):
|
||
"""Test that fancy characters are replaced with ASCII equivalents"""
|
||
input_text = "This has 'fancy' “quotes” and—dashes"
|
||
expected = "This has 'fancy' \"quotes\" and-dashes"
|
||
self.assertEqual(normalize_text(input_text), expected)
|
||
|
||
def test_markdown1(self):
|
||
"""Test that fancy characters are replaced with ASCII equivalents"""
|
||
input_text = "this is *bold*"
|
||
expected = "this is bold"
|
||
self.assertEqual(normalize_text(input_text), expected)
|
||
|
||
def test_markdown2(self):
|
||
"""Test that fancy characters are replaced with ASCII equivalents"""
|
||
input_text = "_italic__ is *bold*"
|
||
expected = "italic_ is bold"
|
||
self.assertEqual(normalize_text(input_text), expected)
|
||
|
||
def test_empty_input(self):
|
||
"""Test that empty input returns empty output"""
|
||
self.assertEqual(normalize_text(""), "")
|
||
|
||
def test_brs(self):
|
||
"""Test that empty input returns empty output"""
|
||
self.assertEqual(normalize_text("Hello<br>everyone"), "Hello everyone")
|
||
self.assertEqual(normalize_text("Hello<br>everyone"), normalize_text("Hello\neveryone"))
|
||
self.assertEqual(normalize_text("Hello<br/>everyone"), "Hello everyone")
|
||
self.assertEqual(normalize_text("Hello<br/>everyone"), normalize_text("Hello\neveryone"))
|
||
|
||
def test_two_stars(self):
|
||
self.assertEqual(
|
||
normalize_text(
|
||
"**Georges V.** (2007) – *Le Forez du VIe au IVe millénaire av. J.-C. Territoires, identités et stratégies des sociétés humaines du Massif central dans le bassin amont de la Loire (France)*, thèse de doctorat, université de Bourgogne, Dijon, 2 vol., 435 p."
|
||
),
|
||
"Georges V. (2007) - Le Forez du VIe au IVe millénaire av. J.-C. Territoires, identités et stratégies des sociétés humaines du Massif central dans le bassin amont de la Loire (France), thèse de doctorat, université de Bourgogne, Dijon, 2 vol., 435 p.",
|
||
)
|
||
|
||
|
||
class TestBasePDFTest(unittest.TestCase):
|
||
"""Test the BasePDFTest class"""
|
||
|
||
def test_valid_initialization(self):
|
||
"""Test that a valid initialization works"""
|
||
test = BasePDFTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value)
|
||
self.assertEqual(test.pdf, "test.pdf")
|
||
self.assertEqual(test.page, 1)
|
||
self.assertEqual(test.id, "test_id")
|
||
self.assertEqual(test.type, TestType.BASELINE.value)
|
||
self.assertEqual(test.max_diffs, 0)
|
||
self.assertIsNone(test.checked)
|
||
self.assertIsNone(test.url)
|
||
|
||
def test_empty_pdf(self):
|
||
"""Test that empty PDF raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
BasePDFTest(pdf="", page=1, id="test_id", type=TestType.BASELINE.value)
|
||
|
||
def test_empty_id(self):
|
||
"""Test that empty ID raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
BasePDFTest(pdf="test.pdf", page=1, id="", type=TestType.BASELINE.value)
|
||
|
||
def test_negative_max_diffs(self):
|
||
"""Test that negative max_diffs raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
BasePDFTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value, max_diffs=-1)
|
||
|
||
def test_invalid_test_type(self):
|
||
"""Test that invalid test type raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
BasePDFTest(pdf="test.pdf", page=1, id="test_id", type="invalid_type")
|
||
|
||
def test_run_method_not_implemented(self):
|
||
"""Test that run method raises NotImplementedError"""
|
||
test = BasePDFTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value)
|
||
with self.assertRaises(NotImplementedError):
|
||
test.run("content")
|
||
|
||
def test_checked_enum(self):
|
||
"""Test that checked accepts valid TestChecked enums"""
|
||
test = BasePDFTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value, checked=TestChecked.VERIFIED)
|
||
self.assertEqual(test.checked, TestChecked.VERIFIED)
|
||
|
||
|
||
class TestTextPresenceTest(unittest.TestCase):
|
||
"""Test the TextPresenceTest class"""
|
||
|
||
def test_valid_present_test(self):
|
||
"""Test that a valid PRESENT test initializes correctly"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, text="test text")
|
||
self.assertEqual(test.text, "test text")
|
||
self.assertTrue(test.case_sensitive)
|
||
self.assertIsNone(test.first_n)
|
||
self.assertIsNone(test.last_n)
|
||
|
||
def test_valid_absent_test(self):
|
||
"""Test that a valid ABSENT test initializes correctly"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ABSENT.value, text="test text", case_sensitive=False)
|
||
self.assertEqual(test.text, "test text")
|
||
self.assertFalse(test.case_sensitive)
|
||
|
||
def test_empty_text(self):
|
||
"""Test that empty text raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, text="")
|
||
|
||
def test_present_text_exact_match(self):
|
||
"""Test that PRESENT test returns True for exact match"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, text="target text")
|
||
result, _ = test.run("This is some target text in a document")
|
||
self.assertTrue(result)
|
||
|
||
def test_present_text_not_found(self):
|
||
"""Test that PRESENT test returns False when text not found"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, text="missing text")
|
||
result, explanation = test.run("This document doesn't have the target")
|
||
self.assertFalse(result)
|
||
self.assertIn("missing text", explanation)
|
||
|
||
def test_present_text_with_max_diffs(self):
|
||
"""Test that PRESENT test with max_diffs handles fuzzy matching"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, text="target text", max_diffs=2)
|
||
result, _ = test.run("This is some targett textt in a document")
|
||
self.assertTrue(result)
|
||
|
||
def test_absent_text_found(self):
|
||
"""Test that ABSENT test returns False when text is found"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ABSENT.value, text="target text")
|
||
result, explanation = test.run("This is some target text in a document")
|
||
self.assertFalse(result)
|
||
self.assertIn("target text", explanation)
|
||
|
||
def test_absent_text_found_diffs(self):
|
||
"""Test that ABSENT test returns False when text is found"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ABSENT.value, text="target text", max_diffs=2)
|
||
result, explanation = test.run("This is some target text in a document")
|
||
self.assertFalse(result)
|
||
result, explanation = test.run("This is some targett text in a document")
|
||
self.assertFalse(result)
|
||
result, explanation = test.run("This is some targettt text in a document")
|
||
self.assertFalse(result)
|
||
result, explanation = test.run("This is some targetttt text in a document")
|
||
self.assertTrue(result)
|
||
|
||
def test_absent_test_subsets(self):
|
||
"""Test that ABSENT test returns False when text is found"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ABSENT.value, text="max", max_diffs=0)
|
||
result, explanation = test.run("There is a max set of diffs to make")
|
||
self.assertFalse(result)
|
||
result, explanation = test.run("This is some maximum text in a document")
|
||
self.assertFalse(result)
|
||
|
||
def test_absent_text_not_found(self):
|
||
"""Test that ABSENT test returns True when text is not found"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ABSENT.value, text="missing text")
|
||
result, _ = test.run("This document doesn't have the target")
|
||
self.assertTrue(result)
|
||
|
||
def test_case_insensitive_present(self):
|
||
"""Test that case_sensitive=False works for PRESENT test"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, text="TARGET TEXT", case_sensitive=False)
|
||
result, _ = test.run("This is some target text in a document")
|
||
self.assertTrue(result)
|
||
|
||
def test_case_insensitive_absent(self):
|
||
"""Test that case_sensitive=False works for ABSENT test"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ABSENT.value, text="TARGET TEXT", case_sensitive=False)
|
||
result, explanation = test.run("This is some target text in a document")
|
||
self.assertFalse(result)
|
||
|
||
def test_first_n_limit(self):
|
||
"""Test that first_n parameter works correctly"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, text="beginning", first_n=20)
|
||
result, _ = test.run("beginning of text, but not the end")
|
||
self.assertTrue(result)
|
||
|
||
# Test that text beyond first_n isn't matched
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, text="end", first_n=20)
|
||
result, _ = test.run("beginning of text, but not the end")
|
||
self.assertFalse(result)
|
||
|
||
def test_last_n_limit(self):
|
||
"""Test that last_n parameter works correctly"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, text="end", last_n=20)
|
||
result, _ = test.run("beginning of text, but not the end")
|
||
self.assertTrue(result)
|
||
|
||
# Test that text beyond last_n isn't matched
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, text="beginning", last_n=20)
|
||
result, _ = test.run("beginning of text, but not the end")
|
||
self.assertFalse(result)
|
||
|
||
def test_both_first_and_last_n(self):
|
||
"""Test that combining first_n and last_n works correctly"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, text="beginning", first_n=15, last_n=10)
|
||
result, _ = test.run("beginning of text, middle part, but not the end")
|
||
self.assertTrue(result)
|
||
|
||
# Text only in middle shouldn't be found
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, text="middle", first_n=15, last_n=10)
|
||
result, _ = test.run("beginning of text, middle part, but not the end")
|
||
self.assertFalse(result)
|
||
|
||
def test_unicode_normalized_forms(self):
|
||
"""Test that e+accent == e_with_accent unicode chars"""
|
||
test = TextPresenceTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, text="I like to eat at a caf\u00e9")
|
||
result, _ = test.run("I like to eat at a caf\u00e9")
|
||
self.assertTrue(result)
|
||
|
||
result, _ = test.run("I like to eat at a cafe\u0301")
|
||
self.assertTrue(result)
|
||
|
||
|
||
class TestTextOrderTest(unittest.TestCase):
|
||
"""Test the TextOrderTest class"""
|
||
|
||
def test_valid_initialization(self):
|
||
"""Test that valid initialization works"""
|
||
test = TextOrderTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ORDER.value, before="first text", after="second text")
|
||
self.assertEqual(test.before, "first text")
|
||
self.assertEqual(test.after, "second text")
|
||
|
||
def test_invalid_test_type(self):
|
||
"""Test that invalid test type raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
TextOrderTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, before="first text", after="second text")
|
||
|
||
def test_empty_before(self):
|
||
"""Test that empty before text raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
TextOrderTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ORDER.value, before="", after="second text")
|
||
|
||
def test_empty_after(self):
|
||
"""Test that empty after text raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
TextOrderTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ORDER.value, before="first text", after="")
|
||
|
||
def test_correct_order(self):
|
||
"""Test that correct order returns True"""
|
||
test = TextOrderTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ORDER.value, before="first", after="second")
|
||
result, _ = test.run("This has first and then second in correct order")
|
||
self.assertTrue(result)
|
||
|
||
def test_incorrect_order(self):
|
||
"""Test that incorrect order returns False"""
|
||
test = TextOrderTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ORDER.value, before="second", after="first")
|
||
result, explanation = test.run("This has first and then second in correct order")
|
||
self.assertFalse(result)
|
||
|
||
def test_before_not_found(self):
|
||
"""Test that 'before' text not found returns False"""
|
||
test = TextOrderTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ORDER.value, before="missing", after="present")
|
||
result, explanation = test.run("This text has present but not the other word")
|
||
self.assertFalse(result)
|
||
|
||
def test_after_not_found(self):
|
||
"""Test that 'after' text not found returns False"""
|
||
test = TextOrderTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ORDER.value, before="present", after="missing")
|
||
result, explanation = test.run("This text has present but not the other word")
|
||
self.assertFalse(result)
|
||
|
||
def test_max_diffs(self):
|
||
"""Test that max_diffs parameter works correctly"""
|
||
test = TextOrderTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ORDER.value, before="first", after="second", max_diffs=1)
|
||
result, _ = test.run("This has firsst and then secand in correct order")
|
||
self.assertTrue(result)
|
||
|
||
def test_multiple_occurrences(self):
|
||
"""Test that multiple occurrences are handled correctly"""
|
||
test = TextOrderTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ORDER.value, before="target", after="target")
|
||
result, _ = test.run("This has target and then target again")
|
||
self.assertTrue(result)
|
||
|
||
# Test reverse direction fails
|
||
test = TextOrderTest(pdf="test.pdf", page=1, id="test_id", type=TestType.ORDER.value, before="B", after="A")
|
||
result, _ = test.run("A B A B") # A comes before B, but B also comes before second A
|
||
self.assertTrue(result)
|
||
|
||
def test_real_case_unicode(self):
|
||
text = """# Marketing Strategies in the Canadian Beef Sector
|
||
|
||
Julieta Frank,
|
||
|
||
Derek Brewin,
|
||
|
||
and
|
||
|
||
Maria José Patiño*
|
||
|
||
*Paper presented at the NCCC-134 Conference on Applied Commodity Price Analysis,
|
||
Forecasting, and Market Risk Management
|
||
St. Louis, Missouri, April 18-19, 2011*
|
||
|
||
*Copyright 2011 by Julieta Frank, Derek Brewin, and Maria José Patiño. All rights reserved.
|
||
Readers may make verbatim copies of this document for non-commercial purposes by any
|
||
means, provided that this copyright notice appears on all such copies.*
|
||
|
||
* Julieta Frank (Julieta_Frank@umanitoba.ca) is an Assistant Professor, Derek Brewin is an Associate Professor, and Maria José Patiño is a graduate student in the Department of Agribusiness and Agricultural Economics at the University of Manitoba. The funding support of the Manitoba Rural Adaptation Council and the Solomon Sinclair Farm Management Institute are gratefully acknowledged."""
|
||
|
||
test = TextOrderTest(
|
||
pdf="test.pdf", page=1, id="test_id", type=TestType.ORDER.value, before="Maria Jos\u00e9 Pati\u00f1o*", after="All rights reserved.", max_diffs=0
|
||
)
|
||
result, _ = test.run(text)
|
||
self.assertTrue(result)
|
||
|
||
|
||
class TestTableTest(unittest.TestCase):
|
||
"""Test the TableTest class"""
|
||
|
||
def setUp(self):
|
||
"""Set up test fixtures"""
|
||
self.markdown_table = """
|
||
| Header 1 | Header 2 | Header 3 |
|
||
| -------- | -------- | -------- |
|
||
| Cell A1 | Cell A2 | Cell A3 |
|
||
| Cell B1 | Cell B2 | Cell B3 |
|
||
"""
|
||
|
||
self.html_table = """
|
||
<table>
|
||
<tr>
|
||
<th>Header 1</th>
|
||
<th>Header 2</th>
|
||
<th>Header 3</th>
|
||
</tr>
|
||
<tr>
|
||
<td>Cell A1</td>
|
||
<td>Cell A2</td>
|
||
<td>Cell A3</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Cell B1</td>
|
||
<td>Cell B2</td>
|
||
<td>Cell B3</td>
|
||
</tr>
|
||
</table>
|
||
"""
|
||
|
||
def test_valid_initialization(self):
|
||
"""Test that valid initialization works"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="target cell")
|
||
self.assertEqual(test.cell, "target cell")
|
||
self.assertEqual(test.up, "")
|
||
self.assertEqual(test.down, "")
|
||
self.assertEqual(test.left, "")
|
||
self.assertEqual(test.right, "")
|
||
self.assertEqual(test.top_heading, "")
|
||
self.assertEqual(test.left_heading, "")
|
||
|
||
def test_invalid_test_type(self):
|
||
"""Test that invalid test type raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, cell="target cell")
|
||
|
||
def test_parse_markdown_tables(self):
|
||
"""Test markdown table parsing"""
|
||
_test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2")
|
||
tables = parse_markdown_tables(self.markdown_table)
|
||
self.assertEqual(len(tables), 1)
|
||
self.assertEqual(tables[0].cell_text[0, 0], "Header 1")
|
||
self.assertEqual(tables[0].cell_text[1, 1], "Cell A2")
|
||
self.assertEqual(tables[0].cell_text[2, 2], "Cell B3")
|
||
|
||
def test_parse_html_tables(self):
|
||
"""Test HTML table parsing"""
|
||
_test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2")
|
||
tables = parse_html_tables(self.html_table)
|
||
self.assertEqual(len(tables), 1)
|
||
self.assertEqual(tables[0].cell_text[0, 0], "Header 1")
|
||
self.assertEqual(tables[0].cell_text[1, 1], "Cell A2")
|
||
self.assertEqual(tables[0].cell_text[2, 2], "Cell B3")
|
||
|
||
def test_match_cell(self):
|
||
"""Test finding a cell in a table"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2")
|
||
result, _ = test.run(self.markdown_table)
|
||
self.assertTrue(result)
|
||
|
||
def test_cell_not_found(self):
|
||
"""Test cell not found in table"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Missing Cell")
|
||
result, explanation = test.run(self.markdown_table)
|
||
self.assertFalse(result)
|
||
self.assertIn("No cell matching", explanation)
|
||
|
||
def test_up_relationship(self):
|
||
"""Test up relationship in table"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2", up="Header 2")
|
||
result, _ = test.run(self.markdown_table)
|
||
self.assertTrue(result)
|
||
|
||
# Test incorrect up relationship
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2", up="Wrong Header")
|
||
result, explanation = test.run(self.markdown_table)
|
||
self.assertFalse(result)
|
||
self.assertIn("doesn't match expected", explanation)
|
||
|
||
def test_down_relationship(self):
|
||
"""Test down relationship in table"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2", down="Cell B2")
|
||
result, _ = test.run(self.markdown_table)
|
||
self.assertTrue(result)
|
||
|
||
# Test incorrect down relationship
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2", down="Wrong Cell")
|
||
result, explanation = test.run(self.markdown_table)
|
||
self.assertFalse(result)
|
||
self.assertIn("doesn't match expected", explanation)
|
||
|
||
def test_left_relationship(self):
|
||
"""Test left relationship in table"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2", left="Cell A1")
|
||
result, _ = test.run(self.markdown_table)
|
||
self.assertTrue(result)
|
||
|
||
# Test incorrect left relationship
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2", left="Wrong Cell")
|
||
result, explanation = test.run(self.markdown_table)
|
||
self.assertFalse(result)
|
||
self.assertIn("doesn't match expected", explanation)
|
||
|
||
def test_right_relationship(self):
|
||
"""Test right relationship in table"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2", right="Cell A3")
|
||
result, _ = test.run(self.markdown_table)
|
||
self.assertTrue(result)
|
||
|
||
# Test incorrect right relationship
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2", right="Wrong Cell")
|
||
result, explanation = test.run(self.markdown_table)
|
||
self.assertFalse(result)
|
||
self.assertIn("doesn't match expected", explanation)
|
||
|
||
def test_top_heading_relationship(self):
|
||
"""Test top_heading relationship in table"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell B2", top_heading="Header 2")
|
||
result, _ = test.run(self.markdown_table)
|
||
self.assertTrue(result)
|
||
|
||
# Test incorrect top_heading relationship
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell B2", top_heading="Wrong Header")
|
||
result, explanation = test.run(self.markdown_table)
|
||
self.assertFalse(result)
|
||
self.assertIn("doesn't match expected", explanation)
|
||
|
||
def test_left_heading_relationship(self):
|
||
"""Test left_heading relationship in table"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A3", left_heading="Cell A1")
|
||
result, _ = test.run(self.markdown_table)
|
||
self.assertTrue(result)
|
||
|
||
# Test incorrect left_heading relationship
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A3", left_heading="Wrong Cell")
|
||
result, explanation = test.run(self.markdown_table)
|
||
self.assertFalse(result)
|
||
self.assertIn("doesn't match expected", explanation)
|
||
|
||
def test_multiple_relationships(self):
|
||
"""Test multiple relationships in table"""
|
||
test = TableTest(
|
||
pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2", up="Header 2", down="Cell B2", left="Cell A1", right="Cell A3"
|
||
)
|
||
result, _ = test.run(self.markdown_table)
|
||
self.assertTrue(result)
|
||
|
||
# Test one incorrect relationship
|
||
test = TableTest(
|
||
pdf="test.pdf",
|
||
page=1,
|
||
id="test_id",
|
||
type=TestType.TABLE.value,
|
||
cell="Cell A2",
|
||
up="Header 2",
|
||
down="Cell B2",
|
||
left="Wrong Cell", # This is incorrect
|
||
right="Cell A3",
|
||
)
|
||
result, explanation = test.run(self.markdown_table)
|
||
self.assertFalse(result)
|
||
self.assertIn("doesn't match expected", explanation)
|
||
|
||
def test_no_tables_found(self):
|
||
"""Test behavior when no tables are found"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2")
|
||
result, explanation = test.run("This is plain text with no tables")
|
||
self.assertFalse(result)
|
||
self.assertEqual(explanation, "No tables found in the content")
|
||
|
||
def test_fuzzy_matching(self):
|
||
"""Test fuzzy matching with max_diffs"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2", max_diffs=1)
|
||
# Create table with slightly misspelled cell
|
||
misspelled_table = self.markdown_table.replace("Cell A2", "Cel A2")
|
||
result, _ = test.run(misspelled_table)
|
||
self.assertTrue(result)
|
||
|
||
def test_with_stripped_content(self):
|
||
"""Test table parsing with stripped content"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2")
|
||
# Strip all leading/trailing whitespace from the markdown table
|
||
stripped_table = self.markdown_table.strip()
|
||
result, explanation = test.run(stripped_table)
|
||
self.assertTrue(result, f"Table test failed with stripped content: {explanation}")
|
||
|
||
def test_table_at_end_of_file(self):
|
||
"""Test that a table at the very end of the file is correctly detected"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2")
|
||
# Create content with text followed by a table at the very end with no trailing newline
|
||
content_with_table_at_end = "Some text before the table.\n" + self.markdown_table.strip()
|
||
result, explanation = test.run(content_with_table_at_end)
|
||
self.assertTrue(result, f"Table at end of file not detected: {explanation}")
|
||
|
||
def test_table_at_end_with_no_trailing_newline(self):
|
||
"""Test that a table at the end with no trailing newline is detected"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2")
|
||
# Remove the trailing newline from the markdown table
|
||
content_without_newline = self.markdown_table.rstrip()
|
||
result, explanation = test.run(content_without_newline)
|
||
self.assertTrue(result, f"Table without trailing newline not detected: {explanation}")
|
||
|
||
def test_table_at_end_with_extra_spaces(self):
|
||
"""Test that a table at the end with extra spaces is detected"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2")
|
||
# Add extra spaces to the end of lines in the table
|
||
lines = self.markdown_table.split("\n")
|
||
content_with_extra_spaces = "\n".join([line + " " for line in lines])
|
||
result, explanation = test.run(content_with_extra_spaces)
|
||
self.assertTrue(result, f"Table with extra spaces not detected: {explanation}")
|
||
|
||
def test_table_at_end_with_mixed_whitespace(self):
|
||
"""Test that a table at the end with mixed whitespace is detected"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2")
|
||
# Add various whitespace characters to the table
|
||
content_with_mixed_whitespace = "Some text before the table.\n" + self.markdown_table.strip() + " \t "
|
||
result, explanation = test.run(content_with_mixed_whitespace)
|
||
self.assertTrue(result, f"Table with mixed whitespace not detected: {explanation}")
|
||
|
||
def test_malformed_table_at_end(self):
|
||
"""Test that a slightly malformed table at the end is still detected"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2")
|
||
# Create a table with irregular pipe placement at the end
|
||
malformed_table = """
|
||
Some text before the table.
|
||
| Header 1 | Header 2 | Header 3
|
||
| -------- | -------- | --------
|
||
| Cell A1 | Cell A2 | Cell A3 |
|
||
| Cell B1 | Cell B2 | Cell B3"""
|
||
result, explanation = test.run(malformed_table)
|
||
self.assertTrue(result, f"Malformed table at end not detected: {explanation}")
|
||
|
||
def test_incomplete_table_at_end(self):
|
||
"""Test that an incomplete table at the end still gets detected if it contains valid rows"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2")
|
||
# Missing the separator row
|
||
incomplete_table = """
|
||
Some text before the table.
|
||
| Header 1 | Header 2 | Header 3 |
|
||
| Cell A1 | Cell A2 | Cell A3 |
|
||
| Cell B1 | Cell B2 | Cell B3 |"""
|
||
result, explanation = test.run(incomplete_table)
|
||
self.assertTrue(result, f"Incomplete table at end not detected: {explanation}")
|
||
|
||
def test_table_with_excessive_blank_lines_at_end(self):
|
||
"""Test that a table followed by many blank lines is detected"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2")
|
||
# Add many blank lines after the table
|
||
table_with_blanks = self.markdown_table + "\n\n\n\n\n\n\n\n\n\n"
|
||
result, explanation = test.run(table_with_blanks)
|
||
self.assertTrue(result, f"Table with blank lines at end not detected: {explanation}")
|
||
|
||
def test_table_at_end_after_long_text(self):
|
||
"""Test that a table at the end after a very long text is detected"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2")
|
||
# Create a very long text before the table
|
||
long_text = "Lorem ipsum dolor sit amet, " * 100
|
||
content_with_long_text = long_text + "\n" + self.markdown_table.strip()
|
||
result, explanation = test.run(content_with_long_text)
|
||
self.assertTrue(result, f"Table after long text not detected: {explanation}")
|
||
|
||
def test_valid_table_at_eof_without_newline(self):
|
||
"""Test that a valid table at EOF without a trailing newline is detected"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Cell A2")
|
||
# Valid table but without trailing newline at the very end of the file
|
||
valid_table_eof = """
|
||
| Header 1 | Header 2 | Header 3 |
|
||
| -------- | -------- | -------- |
|
||
| Cell A1 | Cell A2 | Cell A3 |
|
||
| Cell B1 | Cell B2 | Cell B3 |""".strip()
|
||
result, explanation = test.run(valid_table_eof)
|
||
self.assertTrue(result, f"Valid table at EOF without newline not detected: {explanation}")
|
||
|
||
def test_normalizing(self):
|
||
table = """| Question - – Satisfaction on scale of 10 | Response | Resident Sample | Business Sample |
|
||
|----------------------------------------|----------|----------------|-----------------|
|
||
| Planning for and managing residential, commercial and industrial development | Rating of 8, 9 or 10 | 13% | 11% |
|
||
| | Average rating | 6.4 | 5.7 |
|
||
| | Don’t know responses | 11% | 6% |
|
||
| Environmental protection, support for green projects (e.g. green grants, building retrofits programs, zero waste) | Rating of 8, 9 or 10 | 35% | 34% |
|
||
| | Average rating | 8.0 | 7.5 |
|
||
| | Don’t know responses | 8% | 6% |
|
||
| Providing and maintaining parks and green spaces | Rating of 8, 9 or 10 | 42% | 41% |
|
||
| | Average rating | 7.7 | 7.3 |
|
||
| | Don’t know responses | 1% | 1% |"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="6%", top_heading="Business\nSample")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
def test_mathematical_minus(self):
|
||
table = """| Response | Chinese experimenter | White experimenter |
|
||
|----------|----------------------|--------------------|
|
||
| | Divided attention | Full attention | Divided attention | Full attention |
|
||
| Nonverbal| −.34 (.22) | .54* (.17) | .12 (.27) | −.20 (.24) |
|
||
| Verbal | −.25 (.23) | .36 (.20) | .12 (.27) | −.34 (.22) |
|
||
"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="-.34 (.22)")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
def test_markdown_marker(self):
|
||
table = """| CATEGORY | POINTS EARNED |
|
||
|------------------------------|------------------|
|
||
| Sustainable Sites | 9 |
|
||
| Water Efficiency | 3 |
|
||
| Energy & Atmosphere | 12 |
|
||
| Materials & Resources | 6 |
|
||
| Indoor Environmental Quality | 11 |
|
||
| Innovation & Design Process | 5 |
|
||
| TOTAL | 46 |"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="9", up="POINTS EARNED")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
def test_diffs(self):
|
||
table = """| CATEGORY | POINTS EARNED |
|
||
|------------------------------|------------------|
|
||
| Sustainable Sites | 9 |
|
||
| Water Efficiency | 3 |
|
||
| Energy & Atmosphere | 12 |
|
||
| Materials & Resources | 6 |
|
||
| Indoor Environmental Quality | 11 |
|
||
| Innovation & Design Process | 5 |
|
||
| TOTAL | 46 |"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="9", left="Sustl Sie", max_diffs=2)
|
||
result, explanation = test.run(table)
|
||
self.assertFalse(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="9", left="Sustainable Site", max_diffs=2)
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
def test_markdown_marker2(self):
|
||
table = """| Concentration
|
||
level | [CO] | [SO2] | [NOx] |
|
||
|------------------------|-----------|-------|----------|
|
||
| Control | 0 μM | 0 μM | 0 nM |
|
||
| Low | 250
|
||
μM | 8 μM | 0.002 nM |
|
||
| Medium | 625 μM | 20 μM | 0.005 nM |
|
||
| High | 1250 μM | 40 μM | 0.01 nM |"""
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="20 μM", up=".002 nM")
|
||
result, explanation = test.run(table)
|
||
self.assertFalse(result, explanation)
|
||
|
||
def test_marker3(self):
|
||
table = """| | N | Minimum | Maximum | Gemiddelde | Sd |
|
||
|-----------------------------------------------|-------|---------|---------|------------|-----|
|
||
| Slaapkwaliteit tijdens
|
||
gewone nachten | 2017 | 1,0 | 6,0 | 3,9 | 1,0 |
|
||
| Slaapkwaliteit tijdens
|
||
consignatiediensten | 19816 | 1,0 | 6,0 | 2,8 | 1,2 |
|
||
"""
|
||
test = TableTest(
|
||
pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="2,8", left_heading="Slaapkwaliteit tijdens\nconsignatiediensten"
|
||
)
|
||
result, explanation = test.run(table)
|
||
self.assertFalse(result, explanation)
|
||
|
||
def test_big_table(self):
|
||
table = """ <table>
|
||
<caption>Base: Resident respondents (n=1,315) and Business respondents (n=397)</caption>
|
||
<thead>
|
||
<tr>
|
||
<th>Question – Satisfaction on scale of 10</th>
|
||
<th>Response</th>
|
||
<th>Resident Sample</th>
|
||
<th>Business Sample</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<!-- First category -->
|
||
<tr class="category-row">
|
||
<td rowspan="3">Planning for and managing residential, commercial and industrial development</td>
|
||
<td>Rating of 8, 9 or 10</td>
|
||
<td>13%</td>
|
||
<td>11%</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="subcategory">Average rating</td>
|
||
<td>6.4</td>
|
||
<td>5.7</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="subcategory">Don't know responses</td>
|
||
<td>11%</td>
|
||
<td>6%</td>
|
||
</tr>
|
||
|
||
<!-- Second category -->
|
||
<tr class="category-row">
|
||
<td rowspan="3">Environmental protection, support for green projects (e.g. green grants, building retrofits programs, zero waste)</td>
|
||
<td>Rating of 8, 9 or 10</td>
|
||
<td>35%</td>
|
||
<td>34%</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="subcategory">Average rating</td>
|
||
<td>8.0</td>
|
||
<td>7.5</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="subcategory">Don't know responses</td>
|
||
<td>8%</td>
|
||
<td>6%</td>
|
||
</tr>
|
||
|
||
<!-- Third category -->
|
||
<tr class="category-row">
|
||
<td rowspan="3">Providing and maintaining parks and green spaces</td>
|
||
<td>Rating of 8, 9 or 10</td>
|
||
<td>42%</td>
|
||
<td>41%</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="subcategory">Average rating</td>
|
||
<td>7.7</td>
|
||
<td>7.3</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="subcategory">Don't know responses</td>
|
||
<td>1%</td>
|
||
<td>1%</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
"""
|
||
test = TableTest(
|
||
pdf="test.pdf",
|
||
page=1,
|
||
id="test_id",
|
||
type=TestType.TABLE.value,
|
||
max_diffs=5,
|
||
cell="Planning for and managing residential, commercial and industrial development",
|
||
down="Environmental protection,\nsupport for green projects\n(e.g. green grants,\nbuilding retrofits programs,\nzero waste)",
|
||
)
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
def test_html_rowspans_colspans(self):
|
||
table = """ <table>
|
||
<thead>
|
||
<tr>
|
||
<th rowspan="2">Product Category</th>
|
||
<th rowspan="2">Product Subcategory</th>
|
||
<th colspan="4">Quarterly Sales ($000s)</th>
|
||
<th rowspan="2">Annual Total</th>
|
||
</tr>
|
||
<tr>
|
||
<th>Q1</th>
|
||
<th>Q2</th>
|
||
<th>Q3</th>
|
||
<th>Q4</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="category">
|
||
<td rowspan="4">Electronics</td>
|
||
<td>Smartphones</td>
|
||
<td>245</td>
|
||
<td>278</td>
|
||
<td>312</td>
|
||
<td>389</td>
|
||
<td>1,224</td>
|
||
</tr>
|
||
<tr class="subcategory">
|
||
<td>Laptops</td>
|
||
<td>187</td>
|
||
<td>192</td>
|
||
<td>243</td>
|
||
<td>297</td>
|
||
<td>919</td>
|
||
</tr>
|
||
<tr class="subcategory">
|
||
<td>Tablets</td>
|
||
<td>95</td>
|
||
<td>123</td>
|
||
<td>135</td>
|
||
<td>156</td>
|
||
<td>509</td>
|
||
</tr>
|
||
<tr class="subcategory">
|
||
<td>Accessories</td>
|
||
<td>64</td>
|
||
<td>72</td>
|
||
<td>87</td>
|
||
<td>105</td>
|
||
<td>328</td>
|
||
</tr>
|
||
<tr class="category">
|
||
<td rowspan="3">Home Appliances</td>
|
||
<td>Refrigerators</td>
|
||
<td>132</td>
|
||
<td>145</td>
|
||
<td>151</td>
|
||
<td>162</td>
|
||
<td>590</td>
|
||
</tr>
|
||
<tr class="subcategory">
|
||
<td>Washing Machines</td>
|
||
<td>98</td>
|
||
<td>112</td>
|
||
<td>127</td>
|
||
<td>143</td>
|
||
<td>480</td>
|
||
</tr>
|
||
<tr class="subcategory">
|
||
<td>Microwaves</td>
|
||
<td>54</td>
|
||
<td>67</td>
|
||
<td>72</td>
|
||
<td>84</td>
|
||
<td>277</td>
|
||
</tr>
|
||
<tr class="category">
|
||
<td rowspan="3">Furniture</td>
|
||
<td>Sofas</td>
|
||
<td>112</td>
|
||
<td>128</td>
|
||
<td>134</td>
|
||
<td>142</td>
|
||
<td>516</td>
|
||
</tr>
|
||
<tr class="subcategory">
|
||
<td>Tables</td>
|
||
<td>87</td>
|
||
<td>95</td>
|
||
<td>103</td>
|
||
<td>124</td>
|
||
<td>409</td>
|
||
</tr>
|
||
<tr class="subcategory">
|
||
<td>Chairs</td>
|
||
<td>76</td>
|
||
<td>84</td>
|
||
<td>92</td>
|
||
<td>110</td>
|
||
<td>362</td>
|
||
</tr>
|
||
<tr class="total">
|
||
<td colspan="2">Quarterly Totals</td>
|
||
<td>1,150</td>
|
||
<td>1,296</td>
|
||
<td>1,456</td>
|
||
<td>1,712</td>
|
||
<td>5,614</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>"""
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Refrigerators", left="Home Appliances")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Washing Machines", left="Home Appliances")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Microwaves", left="Home Appliances")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Sofas", top_heading="Product Subcategory")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="135", top_heading="Q3")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="135", top_heading="Quarterly Sales ($000s)")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="1,712", top_heading="Quarterly Sales ($000s)")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="135", top_heading="Q2")
|
||
result, explanation = test.run(table)
|
||
self.assertFalse(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="135", top_heading="Q1")
|
||
result, explanation = test.run(table)
|
||
self.assertFalse(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="135", top_heading="Q4")
|
||
result, explanation = test.run(table)
|
||
self.assertFalse(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Home Appliances", top_heading="Product Category")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Washing Machines", top_heading="Product Category")
|
||
result, explanation = test.run(table)
|
||
self.assertFalse(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Washing Machines", top_heading="Q3")
|
||
result, explanation = test.run(table)
|
||
self.assertFalse(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Washing Machines", top_heading="Quarterly Sales ($000s)")
|
||
result, explanation = test.run(table)
|
||
self.assertFalse(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Electronics", right="Laptops")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Electronics", right="Accessories")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Quarterly Sales ($000s)", down="Q2")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Q2", up="Quarterly Sales ($000s)")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
def test_multiple_markdown_tables(self):
|
||
"""Test that we can find and verify cells in multiple markdown tables in one document"""
|
||
content = """
|
||
# First Table
|
||
|
||
| Name | Age | Role |
|
||
| ---- | --- | ---- |
|
||
| John | 28 | Developer |
|
||
| Jane | 32 | Designer |
|
||
| Bob | 45 | Manager |
|
||
|
||
Some text between tables...
|
||
|
||
# Second Table
|
||
|
||
| Department | Budget | Employees |
|
||
| ---------- | ------ | --------- |
|
||
| Engineering | 1.2M | 15 |
|
||
| Design | 0.8M | 8 |
|
||
| Marketing | 1.5M | 12 |
|
||
| HR | 0.5M | 5 |
|
||
"""
|
||
|
||
# Test cells in the first table
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="John", right="28")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="32", left="Jane")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
# Test cells in the second table
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Engineering", right="1.2M")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="12", left="1.5M")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
# Verify top headings work correctly across tables
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Bob", top_heading="Name")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="HR", top_heading="Department")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
def test_multiple_html_tables(self):
|
||
"""Test that we can find and verify cells in multiple HTML tables in one document"""
|
||
content = """
|
||
<h1>First Table</h1>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Country</th>
|
||
<th>Capital</th>
|
||
<th>Population</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>USA</td>
|
||
<td>Washington DC</td>
|
||
<td>331M</td>
|
||
</tr>
|
||
<tr>
|
||
<td>France</td>
|
||
<td>Paris</td>
|
||
<td>67M</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Japan</td>
|
||
<td>Tokyo</td>
|
||
<td>126M</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
|
||
<p>Some text between tables...</p>
|
||
|
||
<h1>Second Table</h1>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr>
|
||
<th>Company</th>
|
||
<th>Industry</th>
|
||
<th>Revenue</th>
|
||
<th>Employees</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td>ABC Corp</td>
|
||
<td>Technology</td>
|
||
<td>$5B</td>
|
||
<td>10,000</td>
|
||
</tr>
|
||
<tr>
|
||
<td>XYZ Inc</td>
|
||
<td>Healthcare</td>
|
||
<td>$2.5B</td>
|
||
<td>8,500</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Acme Co</td>
|
||
<td>Manufacturing</td>
|
||
<td>$1.8B</td>
|
||
<td>15,000</td>
|
||
</tr>
|
||
<tr>
|
||
<td>Global LLC</td>
|
||
<td>Finance</td>
|
||
<td>$3.2B</td>
|
||
<td>6,200</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
"""
|
||
|
||
# Test cells in the first table
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="USA", right="Washington DC")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="126M", left="Tokyo")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
# Test cells in the second table
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="XYZ Inc", right="Healthcare")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="15,000", left="$1.8B")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
# Verify top headings work correctly across tables
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Tokyo", top_heading="Capital")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Finance", top_heading="Industry")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
def test_mixed_markdown_and_html_tables(self):
|
||
"""Test that we can find and verify cells in mixed markdown and HTML tables in one document"""
|
||
content = """
|
||
# Markdown Table
|
||
|
||
| Product | Price | Quantity |
|
||
| ------- | ----- | -------- |
|
||
| Apple | $1.20 | 100 |
|
||
| Orange | $0.80 | 150 |
|
||
| Banana | $0.60 | 200 |
|
||
|
||
<h1>HTML Table</h1>
|
||
|
||
<table>
|
||
<tr>
|
||
<th>Month</th>
|
||
<th>Income</th>
|
||
<th>Expenses</th>
|
||
<th>Profit</th>
|
||
</tr>
|
||
<tr>
|
||
<td>January</td>
|
||
<td>$10,000</td>
|
||
<td>$8,000</td>
|
||
<td>$2,000</td>
|
||
</tr>
|
||
<tr>
|
||
<td>February</td>
|
||
<td>$12,000</td>
|
||
<td>$9,500</td>
|
||
<td>$2,500</td>
|
||
</tr>
|
||
<tr>
|
||
<td>March</td>
|
||
<td>$15,000</td>
|
||
<td>$10,200</td>
|
||
<td>$4,800</td>
|
||
</tr>
|
||
</table>
|
||
"""
|
||
|
||
# Test cells in the markdown table
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Orange", right="$0.80")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
# Test cells in the HTML table
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="February", right="$12,000")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
# Verify we can find cells with specific top headings
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="100", top_heading="Quantity")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="$4,800", top_heading="Profit")
|
||
result, explanation = test.run(content)
|
||
self.assertTrue(result, explanation)
|
||
|
||
def test_br_tags_replacement(self):
|
||
"""Test that <br> and <br/> tags are correctly replaced with newlines"""
|
||
table = """<table>
|
||
<tr>
|
||
<th>Header 1</th>
|
||
<th>Header 2</th>
|
||
</tr>
|
||
<tr>
|
||
<td>Line 1<br/>Line 2<br/>Line 3</td>
|
||
<td>Single line</td>
|
||
</tr>
|
||
</table>"""
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="Line 1 Line 2 Line 3")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
def test_real_complicated_table(self):
|
||
table = """ <table>
|
||
<thead>
|
||
<tr>
|
||
<th colspan="7">Table 1 Differences in diagnoses, gender and family status for participants with a suicide attempt and those without a suicide attempt within the 12-month follow-up interval</th>
|
||
</tr>
|
||
<tr class="header-row">
|
||
<th rowspan="2"></th>
|
||
<th colspan="2">Participants with no<br>suicide attempt<br>(n = 132)<sup>a</sup></th>
|
||
<th colspan="2">Participants with a<br>suicide attempt<br>(n = 43)<sup>b</sup></th>
|
||
<th colspan="3"></th>
|
||
</tr>
|
||
<tr class="header-row">
|
||
<th>n</th>
|
||
<th>%</th>
|
||
<th>n</th>
|
||
<th>%</th>
|
||
<th>χ<sup>2</sup></th>
|
||
<th>d.f.</th>
|
||
<th>P</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr>
|
||
<td class="section-header">ICD-10 diagnoses</td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td> F0</td>
|
||
<td>1</td>
|
||
<td>0.76</td>
|
||
<td>0</td>
|
||
<td>0.00</td>
|
||
<td>0.00</td>
|
||
<td>1</td>
|
||
<td>1.00</td>
|
||
</tr>
|
||
<tr>
|
||
<td> F1</td>
|
||
<td>17</td>
|
||
<td>12.88</td>
|
||
<td>12</td>
|
||
<td>27.91</td>
|
||
<td>4.39</td>
|
||
<td>1</td>
|
||
<td>0.04</td>
|
||
</tr>
|
||
<tr>
|
||
<td> F2</td>
|
||
<td>1</td>
|
||
<td>0.76</td>
|
||
<td>0</td>
|
||
<td>0.00</td>
|
||
<td>0.00</td>
|
||
<td>1</td>
|
||
<td>1.00</td>
|
||
</tr>
|
||
<tr>
|
||
<td> F3</td>
|
||
<td>106</td>
|
||
<td>80.30</td>
|
||
<td>31</td>
|
||
<td>72.09</td>
|
||
<td>0.74</td>
|
||
<td>1</td>
|
||
<td>0.39</td>
|
||
</tr>
|
||
<tr>
|
||
<td> F4</td>
|
||
<td>42</td>
|
||
<td>31.82</td>
|
||
<td>17</td>
|
||
<td>39.53</td>
|
||
<td>0.61</td>
|
||
<td>1</td>
|
||
<td>0.43</td>
|
||
</tr>
|
||
<tr>
|
||
<td> F5</td>
|
||
<td>5</td>
|
||
<td>3.79</td>
|
||
<td>5</td>
|
||
<td>11.63</td>
|
||
<td>2.44</td>
|
||
<td>1</td>
|
||
<td>0.12</td>
|
||
</tr>
|
||
<tr>
|
||
<td> F6</td>
|
||
<td>20</td>
|
||
<td>15.15</td>
|
||
<td>19</td>
|
||
<td>44.19</td>
|
||
<td>14.48</td>
|
||
<td>1</td>
|
||
<td>0.00</td>
|
||
</tr>
|
||
<tr>
|
||
<td> F7</td>
|
||
<td>0</td>
|
||
<td>0.00</td>
|
||
<td>0</td>
|
||
<td>0.00</td>
|
||
<td>—</td>
|
||
<td>—</td>
|
||
<td>—</td>
|
||
</tr>
|
||
<tr>
|
||
<td> F8</td>
|
||
<td>1</td>
|
||
<td>0.76</td>
|
||
<td>0</td>
|
||
<td>0.00</td>
|
||
<td>0.00</td>
|
||
<td>1</td>
|
||
<td>1.00</td>
|
||
</tr>
|
||
<tr>
|
||
<td> F9</td>
|
||
<td>2</td>
|
||
<td>1.52</td>
|
||
<td>1</td>
|
||
<td>2.33</td>
|
||
<td>0.00</td>
|
||
<td>1</td>
|
||
<td>1.00</td>
|
||
</tr>
|
||
<tr>
|
||
<td class="section-header">Gender</td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
<td>3.09</td>
|
||
<td>2</td>
|
||
<td>0.21</td>
|
||
</tr>
|
||
<tr>
|
||
<td> Female</td>
|
||
<td>75</td>
|
||
<td>56.8</td>
|
||
<td>24</td>
|
||
<td>55.8</td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td> Male</td>
|
||
<td>57</td>
|
||
<td>43.2</td>
|
||
<td>18</td>
|
||
<td>41.9</td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td> Diverse</td>
|
||
<td>0</td>
|
||
<td>0</td>
|
||
<td>1</td>
|
||
<td>2.3</td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td class="section-header">Family status</td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
<td>4.87</td>
|
||
<td>4</td>
|
||
<td>0.30</td>
|
||
</tr>
|
||
<tr>
|
||
<td> Single</td>
|
||
<td>55</td>
|
||
<td>41.7</td>
|
||
<td>14</td>
|
||
<td>32.6</td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td> Partnership</td>
|
||
<td>25</td>
|
||
<td>18.9</td>
|
||
<td>9</td>
|
||
<td>20.9</td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td> Married</td>
|
||
<td>27</td>
|
||
<td>20.5</td>
|
||
<td>5</td>
|
||
<td>11.6</td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td> Divorced</td>
|
||
<td>20</td>
|
||
<td>15.2</td>
|
||
<td>11</td>
|
||
<td>25.6</td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
<tr>
|
||
<td> Widowed</td>
|
||
<td>1</td>
|
||
<td>0.8</td>
|
||
<td>1</td>
|
||
<td>2.3</td>
|
||
<td></td>
|
||
<td></td>
|
||
<td></td>
|
||
</tr>
|
||
</tbody>
|
||
<tfoot>
|
||
<tr>
|
||
<td colspan="8" class="footnote">
|
||
F0: Organic, including symptomatic, mental disorders; F1: Mental and behavioural disorders due to psychoactive substance use; F2: Schizophrenia, schizotypal and delusional disorders; F3: affective disorders; F4: Neurotic, stress-related and somatoform disorders; F5: Behavioural syndromes associated with physiological disturbances and physical factors; F6: Disorders of adult personality and behaviour; F7: Mental retardation; F8: Disorders of psychological development; F9: Behavioural and emotional disorders with onset usually occurring in childhood and adolescence.<br>
|
||
a. 75.43% of the total sample with full information on suicide reattempts within the entire 12-month follow-up interval.<br>
|
||
b. 24.57% of the total sample with full information on suicide reattempts within the entire 12-month follow-up interval.
|
||
</td>
|
||
</tr>
|
||
</tfoot>
|
||
</table>"""
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="4.39", top_heading="χ2")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="12.88", top_heading="%")
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
# Account for the superscript in the header
|
||
test = TableTest(
|
||
pdf="test.pdf", page=1, id="test_id", type=TestType.TABLE.value, cell="12.88", top_heading="Participants with no suicide attempt (n = 132)a"
|
||
)
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
test = TableTest(
|
||
pdf="test.pdf",
|
||
page=1,
|
||
id="test_id",
|
||
type=TestType.TABLE.value,
|
||
cell="12.88",
|
||
top_heading="Table 1 Differences in diagnoses, gender and family status for participants with a suicide attempt and those without a suicide attempt within the 12-month follow-up interval",
|
||
)
|
||
result, explanation = test.run(table)
|
||
self.assertTrue(result, explanation)
|
||
|
||
|
||
class TestBaselineTest(unittest.TestCase):
|
||
"""Test the BaselineTest class"""
|
||
|
||
def test_valid_initialization(self):
|
||
"""Test that valid initialization works"""
|
||
test = BaselineTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value, max_repeats=50)
|
||
self.assertEqual(test.max_repeats, 50)
|
||
|
||
def test_non_empty_content(self):
|
||
"""Test that non-empty content passes"""
|
||
test = BaselineTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value)
|
||
result, _ = test.run("This is some normal content")
|
||
self.assertTrue(result)
|
||
|
||
def test_empty_content(self):
|
||
"""Test that empty content fails"""
|
||
test = BaselineTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value)
|
||
result, explanation = test.run(" \n\t ")
|
||
self.assertFalse(result)
|
||
self.assertIn("no alpha numeric characters", explanation)
|
||
|
||
def test_repeating_content(self):
|
||
"""Test that highly repeating content fails"""
|
||
test = BaselineTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value, max_repeats=2)
|
||
# Create highly repeating content - repeat "abc" many times
|
||
repeating_content = "abc" * 10
|
||
result, explanation = test.run(repeating_content)
|
||
self.assertFalse(result)
|
||
self.assertIn("repeating", explanation)
|
||
|
||
def test_content_with_disallowed_characters(self):
|
||
"""Test that content with disallowed characters fails"""
|
||
test = BaselineTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value)
|
||
result, explanation = test.run("This has Chinese characters: 你好")
|
||
self.assertFalse(result)
|
||
self.assertIn("disallowed characters", explanation)
|
||
|
||
def test_content_with_emoji(self):
|
||
"""Test that content with emoji fails"""
|
||
test = BaselineTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value)
|
||
result, explanation = test.run("This has emoji: 😊")
|
||
self.assertFalse(result)
|
||
self.assertIn("disallowed characters", explanation)
|
||
self.assertIn("😊", explanation)
|
||
|
||
def test_content_with_mandarin(self):
|
||
test = BaselineTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value)
|
||
result, explanation = test.run("asdfasdfas維基百科/中文asdfw")
|
||
self.assertFalse(result)
|
||
self.assertIn("disallowed characters", explanation)
|
||
|
||
def test_valid_content(self):
|
||
"""Test that valid content passes all checks"""
|
||
test = BaselineTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value)
|
||
content = "This is some normal content with proper English letters and no suspicious repetition."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_max_length_with_image_tags_skipped(self):
|
||
"""Test that image tags are properly removed when max_length_skips_image_alt_tags is True"""
|
||
# Test with max_length_skips_image_alt_tags=True
|
||
test = BaselineTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value, max_length=10, max_length_skips_image_alt_tags=True)
|
||
|
||
# Content with image tag that would exceed max_length if counted
|
||
content = "Hello  World"
|
||
result, _ = test.run(content)
|
||
# Should pass because "HelloWorld" has 10 alphanumeric characters after removing image tag
|
||
self.assertTrue(result)
|
||
|
||
def test_max_length_with_image_tags_not_skipped(self):
|
||
"""Test that image tags are counted when max_length_skips_image_alt_tags is False"""
|
||
# Test with max_length_skips_image_alt_tags=False (default)
|
||
test = BaselineTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value, max_length=10)
|
||
|
||
# Same content with image tag
|
||
content = "Hello  World"
|
||
result, explanation = test.run(content)
|
||
# Should fail because full content has way more than 10 alphanumeric characters
|
||
self.assertFalse(result)
|
||
self.assertIn("characters were output for a page we expected to be blank", explanation)
|
||
|
||
def test_multiple_image_tags_removed(self):
|
||
"""Test that multiple image tags are removed correctly"""
|
||
test = BaselineTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value, max_length=15, max_length_skips_image_alt_tags=True)
|
||
|
||
content = "Start  middle  end"
|
||
result, _ = test.run(content)
|
||
# Should pass because "Start middle end" = "Startmiddleend" has 14 alphanumeric characters
|
||
self.assertTrue(result)
|
||
|
||
def test_nested_brackets_in_image_tags(self):
|
||
"""Test that image tags with nested brackets are handled correctly"""
|
||
test = BaselineTest(pdf="test.pdf", page=1, id="test_id", type=TestType.BASELINE.value, max_length=8, max_length_skips_image_alt_tags=True)
|
||
|
||
content = "Text ![Complex [nested] description](image.png) here"
|
||
result, _ = test.run(content)
|
||
# Should pass because "Text here" = "Texthere" has 8 alphanumeric characters
|
||
self.assertTrue(result)
|
||
|
||
|
||
class TestMathTest(unittest.TestCase):
|
||
"""Test the MathTest class"""
|
||
|
||
def test_valid_initialization(self):
|
||
"""Test that valid initialization works"""
|
||
try:
|
||
test = MathTest(pdf="test.pdf", page=1, id="test_id", type=TestType.MATH.value, math="a + b = c")
|
||
self.assertEqual(test.math, "a + b = c")
|
||
except Exception as e:
|
||
self.fail(f"Valid initialization failed with: {e}")
|
||
|
||
def test_invalid_test_type(self):
|
||
"""Test that invalid test type raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
MathTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, math="a + b = c")
|
||
|
||
def test_empty_math(self):
|
||
"""Test that empty math raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
MathTest(pdf="test.pdf", page=1, id="test_id", type=TestType.MATH.value, math="")
|
||
|
||
def test_exact_math_match(self):
|
||
"""Test exact match of math equation"""
|
||
try:
|
||
test = MathTest(pdf="test.pdf", page=1, id="test_id", type=TestType.MATH.value, math="a + b = c")
|
||
|
||
# Test content with exact math match
|
||
content = "Here is an equation: $$a + b = c$$"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
except Exception as e:
|
||
self.fail(f"Test failed with: {e}")
|
||
|
||
def test_rendered_math_match(self):
|
||
"""Test rendered match of math equation"""
|
||
try:
|
||
test = MathTest(pdf="test.pdf", page=1, id="test_id", type=TestType.MATH.value, math="a + b = c")
|
||
|
||
# Test content with different but equivalent math
|
||
content = "Here is an equation: $$a+b=c$$"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
except Exception as e:
|
||
self.fail(f"Test failed with: {e}")
|
||
|
||
def test_no_math_match(self):
|
||
"""Test no match of math equation"""
|
||
try:
|
||
test = MathTest(pdf="test.pdf", page=1, id="test_id", type=TestType.MATH.value, math="a + b = c")
|
||
|
||
# Test content with no matching math
|
||
content = "Here is an equation: $$x + y = z$$"
|
||
result, explanation = test.run(content)
|
||
self.assertFalse(result)
|
||
self.assertIn("No match found", explanation)
|
||
except Exception as e:
|
||
self.fail(f"Test failed with: {e}")
|
||
|
||
def test_different_math_delimiters(self):
|
||
"""Test different math delimiters"""
|
||
try:
|
||
test = MathTest(pdf="test.pdf", page=1, id="test_id", type=TestType.MATH.value, math="a + b = c")
|
||
|
||
# Test different delimiters
|
||
delimiters = [
|
||
"$$a + b = c$$", # $$...$$
|
||
"$a + b = c$", # $...$
|
||
"\\(a + b = c\\)", # \(...\)
|
||
"\\[a + b = c\\]", # \[...\]
|
||
]
|
||
|
||
for delim in delimiters:
|
||
content = f"Here is an equation: {delim}"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
except Exception as e:
|
||
self.fail(f"Test failed with: {e}")
|
||
|
||
|
||
class TestFormatTest(unittest.TestCase):
|
||
"""Test the FormatTest class"""
|
||
|
||
def test_valid_initialization(self):
|
||
"""Test that valid initialization works"""
|
||
test = FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="Important Title", format="heading")
|
||
self.assertEqual(test.text, "Important Title")
|
||
self.assertEqual(test.format, "heading")
|
||
self.assertTrue(test.case_sensitive)
|
||
|
||
def test_invalid_test_type(self):
|
||
"""Test that invalid test type raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, text="test text", format="bold")
|
||
|
||
def test_empty_text(self):
|
||
"""Test that empty text raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="", format="heading")
|
||
|
||
def test_invalid_format_type(self):
|
||
"""Test that invalid format type raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="test text", format="underline") # Not supported
|
||
|
||
def test_markdown_heading(self):
|
||
"""Test detection of markdown headings"""
|
||
test = FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="Chapter One", format="heading")
|
||
|
||
# Test various heading levels
|
||
content = "# Chapter One\nSome text here"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
content = "## Chapter One\nSome text here"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
content = "### Chapter One\nSome text here"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_html_heading(self):
|
||
"""Test detection of HTML headings"""
|
||
test = FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="Chapter One", format="heading")
|
||
|
||
# Test various HTML heading tags
|
||
content = "<h1>Chapter One</h1>\nSome text here"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
content = "<h3>Chapter One</h3>\nSome text here"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_markdown_bold(self):
|
||
"""Test detection of markdown bold text"""
|
||
test = FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="important", format="bold")
|
||
|
||
# Test ** syntax
|
||
content = "This is **important** text"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test __ syntax
|
||
content = "This is __important__ text"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Should not match if not bold
|
||
content = "This is important text"
|
||
result, explanation = test.run(content)
|
||
self.assertFalse(result)
|
||
self.assertIn("not found with bold formatting", explanation)
|
||
|
||
def test_html_bold(self):
|
||
"""Test detection of HTML bold text"""
|
||
test = FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="important", format="bold")
|
||
|
||
# Test <b> tag
|
||
content = "This is <b>important</b> text"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test <strong> tag
|
||
content = "This is <strong>important</strong> text"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_markdown_italic(self):
|
||
"""Test detection of markdown italic text"""
|
||
test = FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="emphasis", format="italic")
|
||
|
||
# Test * syntax
|
||
content = "This needs *emphasis* here"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test _ syntax
|
||
content = "This needs _emphasis_ here"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Should not match bold
|
||
content = "This needs **emphasis** here"
|
||
result, explanation = test.run(content)
|
||
self.assertFalse(result)
|
||
self.assertIn("not found with italic formatting", explanation)
|
||
|
||
def test_html_italic(self):
|
||
"""Test detection of HTML italic text"""
|
||
test = FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="emphasis", format="italic")
|
||
|
||
# Test <i> tag
|
||
content = "This needs <i>emphasis</i> here"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test <em> tag
|
||
content = "This needs <em>emphasis</em> here"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_partial_match(self):
|
||
"""Test that partial matches work"""
|
||
test = FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="Chapter", format="heading")
|
||
|
||
content = "# Chapter One: Introduction\nSome text here"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_case_insensitive(self):
|
||
"""Test case insensitive matching"""
|
||
test = FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="IMPORTANT", format="bold", case_sensitive=False)
|
||
|
||
content = "This is **important** text"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_fuzzy_matching(self):
|
||
"""Test fuzzy matching with max_diffs"""
|
||
test = FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="Chapter One", format="heading", max_diffs=2)
|
||
|
||
# Slightly misspelled heading
|
||
content = "# Chaptre Oen\nSome text here"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_multiple_formatted_sections(self):
|
||
"""Test when multiple sections have the same format"""
|
||
test = FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="Section Two", format="heading")
|
||
|
||
content = """
|
||
# Section One
|
||
Some text
|
||
|
||
## Section Two
|
||
More text
|
||
|
||
### Section Three
|
||
Even more text
|
||
"""
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_nested_formatting(self):
|
||
"""Test nested formatting scenarios"""
|
||
test = FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="bold text inside", format="bold")
|
||
|
||
# Bold text inside a heading
|
||
content = "# This has **bold text inside** the heading"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_mixed_markdown_html(self):
|
||
"""Test content with both markdown and HTML formatting"""
|
||
test = FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="Important", format="bold")
|
||
|
||
content = """
|
||
Some **Important** markdown bold text.
|
||
And some <b>Important</b> HTML bold text.
|
||
"""
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_italic_not_matching_bold(self):
|
||
"""Test that italic patterns don't incorrectly match bold"""
|
||
test = FormatTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FORMAT.value, text="text", format="italic")
|
||
|
||
# Should not match double asterisk bold
|
||
content = "This is **text** here"
|
||
result, _ = test.run(content)
|
||
self.assertFalse(result)
|
||
|
||
# Should match single asterisk italic
|
||
content = "This is *text* here"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_normalization_in_formatted_text(self):
|
||
"""Test that text normalization works within formatted sections"""
|
||
test = FormatTest(
|
||
pdf="test.pdf",
|
||
page=1,
|
||
id="test_id",
|
||
type=TestType.FORMAT.value,
|
||
text="\"fancy\" 'quotes'", # This is what it looks like after normalization
|
||
format="bold",
|
||
)
|
||
|
||
# Content has fancy quotes that should be normalized
|
||
content = "This is **\u201cfancy\u201d \u2018quotes\u2019** here"
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
|
||
class TestFootnoteTest(unittest.TestCase):
|
||
"""Test the FootnoteTest class"""
|
||
|
||
def test_valid_initialization(self):
|
||
"""Test that valid initialization works"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="1")
|
||
self.assertEqual(test.marker, "1")
|
||
self.assertIsNone(test.appears_before_marker)
|
||
self.assertIsNone(test.appears_after_marker)
|
||
|
||
def test_invalid_test_type(self):
|
||
"""Test that invalid test type raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.PRESENT.value, marker="1")
|
||
|
||
def test_marker_required(self):
|
||
"""Test that marker field is required"""
|
||
with self.assertRaises(TypeError):
|
||
FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value)
|
||
|
||
def test_marker_only(self):
|
||
"""Test footnote with only marker specified"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="2")
|
||
|
||
# Test with markdown footnote reference
|
||
content = "This is some text[^2] with a footnote.\n\n[^2]: This is the footnote text."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test with HTML superscript
|
||
content = "This is some text<sup>2</sup> with a footnote."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test with Unicode superscript
|
||
content = "This is some text² with a footnote."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test when marker not found
|
||
content = "This text has no footnote markers."
|
||
result, explanation = test.run(content)
|
||
self.assertFalse(result)
|
||
self.assertIn("not found", explanation)
|
||
|
||
def test_appears_before_marker(self):
|
||
"""Test footnote with appears_before_marker specified"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="1", appears_before_marker="important fact")
|
||
|
||
# Test when text appears before marker
|
||
content = "This is an important fact[^1] in the document."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
content = "This is an important fact [^1] in the document."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
content = "This is an important fact\t[^1] in the document."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test when text doesn't appear before marker
|
||
content = "This is something else[^1] important fact comes later."
|
||
result, explanation = test.run(content)
|
||
self.assertFalse(result)
|
||
self.assertIn("not found before", explanation)
|
||
|
||
def test_appears_after_marker(self):
|
||
"""Test footnote with appears_after_marker specified"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="3", appears_after_marker="that needs citation")
|
||
|
||
# Test when text appears after marker
|
||
content = """This is a statement[^3] that needs citation from the study."""
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
content = """This is a statement[^3]that needs citation from the study."""
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
content = """This is a statement ³that needs citation from the study."""
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test when text doesn't appear after marker
|
||
content = """This is a statement that needs citation before [^3] the marker."""
|
||
result, explanation = test.run(content)
|
||
self.assertFalse(result)
|
||
self.assertIn("not found after", explanation)
|
||
|
||
def test_both_before_and_after(self):
|
||
"""Test footnote with both appears_before_marker and appears_after_marker specified"""
|
||
test = FootnoteTest(
|
||
pdf="test.pdf",
|
||
page=1,
|
||
id="test_id",
|
||
type=TestType.FOOTNOTE.value,
|
||
marker="1",
|
||
appears_before_marker="important fact",
|
||
appears_after_marker="documented here",
|
||
)
|
||
|
||
# Test when both conditions are met (can be at different occurrences)
|
||
content = "This is an important fact[^1] and also[^1] documented here."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test when both conditions are met at same occurrence
|
||
content = "This is an important fact[^1] documented here in the text."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test when one condition is not met
|
||
content = "This is something else[^1] documented here."
|
||
result, explanation = test.run(content)
|
||
self.assertFalse(result)
|
||
|
||
content = "Thiis is something else [^1] and also an important fact [^1] documented here."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_fuzzy_matching_for_appears_before(self):
|
||
"""Test fuzzy matching with max_diffs for appears_before_marker"""
|
||
test = FootnoteTest(
|
||
pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="1", appears_before_marker="Example Reference", max_diffs=3
|
||
)
|
||
|
||
# Slightly misspelled text before marker
|
||
content = """The Exampl Referenc[^1] is cited here."""
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_fuzzy_matching_for_appears_after(self):
|
||
"""Test fuzzy matching with max_diffs for appears_after_marker"""
|
||
test = FootnoteTest(
|
||
pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="2", appears_after_marker="statistical analysis", max_diffs=2
|
||
)
|
||
|
||
# Slightly misspelled text after marker
|
||
content = "The reference[^2] statistcal analysys shows significant results."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_multiple_footnotes(self):
|
||
"""Test handling of multiple footnotes in document"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="2")
|
||
|
||
content = """First claim[^1] and second claim[^2].
|
||
|
||
[^1]: First reference text.
|
||
[^2]: Second reference with more details."""
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="1")
|
||
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_html_superscript_variations(self):
|
||
"""Test various HTML superscript formats"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="4")
|
||
|
||
# Test with attributes in sup tag
|
||
content = 'Text with footnote<sup class="footnote">4</sup>.'
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test case insensitive HTML
|
||
content = "Text with footnote<SUP>4</SUP>."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_unicode_superscripts(self):
|
||
"""Test all Unicode superscript digits"""
|
||
superscripts = {"0": "⁰", "1": "¹", "2": "²", "3": "³", "4": "⁴", "5": "⁵", "6": "⁶", "7": "⁷", "8": "⁸", "9": "⁹"}
|
||
|
||
for digit, superscript in superscripts.items():
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker=digit)
|
||
content = f"Text with footnote{superscript}."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result, f"Failed for superscript {digit}")
|
||
|
||
def test_multi_digit_marker(self):
|
||
"""Test multi-digit footnote markers"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="12")
|
||
|
||
# Test markdown reference
|
||
content = "Text with footnote[^12].\n\n[^12]: Reference text."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test Unicode superscript for multi-digit
|
||
content = "Text with footnote¹²."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_appears_before_ignores_whitespace(self):
|
||
"""Test that appears_before_marker ignores whitespace and non-alpha characters"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="1", appears_before_marker="claim")
|
||
|
||
# Should match even with punctuation and whitespace between
|
||
content = "This is a claim, ... !![^1] here."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Should match with just spaces
|
||
content = "This is a claim [^1] here."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_empty_appears_before_validation(self):
|
||
"""Test that empty appears_before_marker field raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="1", appears_before_marker=" ") # Whitespace only
|
||
|
||
def test_empty_appears_after_validation(self):
|
||
"""Test that empty appears_after_marker field raises ValidationError"""
|
||
with self.assertRaises(ValidationError):
|
||
FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="1", appears_after_marker=" ") # Whitespace only
|
||
|
||
def test_normalized_text_in_footnotes(self):
|
||
"""Test that text normalization works in appears_before/after_marker"""
|
||
test = FootnoteTest(
|
||
pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="1", appears_before_marker='"fancy" quotes and-dashes' # Normalized form
|
||
)
|
||
|
||
# Content with fancy characters that should be normalized
|
||
content = """"fancy" quotes and—dashes[^1] here."""
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_exact_marker_match(self):
|
||
"""Test that marker matching is exact"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="1")
|
||
|
||
# Should match "1" but not "10", "11", etc.
|
||
content = "Text with footnote[^10]."
|
||
result, explanation = test.run(content)
|
||
self.assertFalse(result)
|
||
self.assertIn("not found", explanation)
|
||
|
||
# Should match exactly "1"
|
||
content = "Text with footnote[^1]."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_special_symbol_markers(self):
|
||
"""Test footnote markers with special symbols"""
|
||
# Test common special symbols used as footnote markers
|
||
special_symbols = ["*", "†", "‡", "§", "¶", "∥", "**", "††", "‡‡"]
|
||
|
||
for symbol in special_symbols:
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker=symbol)
|
||
|
||
# Test with markdown footnote reference
|
||
content = f"Text with footnote[^{symbol}].\n\n[^{symbol}]: Footnote text."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result, f"Failed for symbol marker '{symbol}' in markdown")
|
||
|
||
# Test with HTML superscript
|
||
content = f"Text with footnote<sup>{symbol}</sup>."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result, f"Failed for symbol marker '{symbol}' in HTML")
|
||
|
||
# Test that the symbol as plain text doesn't match
|
||
# (it needs to be in proper footnote format)
|
||
content = f"Text with {symbol} in the middle."
|
||
result, _ = test.run(content)
|
||
self.assertFalse(result, f"Plain text '{symbol}' should not match as footnote marker")
|
||
|
||
def test_word_markers(self):
|
||
"""Test footnote markers that are words"""
|
||
word_markers = ["apple", "note", "ref", "A", "B", "foo"]
|
||
|
||
for word in word_markers:
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker=word)
|
||
|
||
# Test with markdown footnote reference
|
||
content = f"Text with footnote[^{word}].\n\n[^{word}]: Footnote text."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result, f"Failed for word marker '{word}' in markdown")
|
||
|
||
# Test with HTML superscript
|
||
content = f"Text with footnote<sup>{word}</sup>."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result, f"Failed for word marker '{word}' in HTML")
|
||
|
||
# Test that the word as plain text doesn't match
|
||
content = f"Text with {word} in the middle."
|
||
result, _ = test.run(content)
|
||
self.assertFalse(result, f"Plain text '{word}' should not match as footnote marker")
|
||
|
||
def test_marker_with_whitespace_validation(self):
|
||
"""Test that markers with whitespace are rejected"""
|
||
with self.assertRaises(ValidationError) as cm:
|
||
FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="has space")
|
||
self.assertIn("cannot contain whitespace", str(cm.exception))
|
||
|
||
def test_special_symbols_with_appears_before(self):
|
||
"""Test special symbol markers combined with appears_before_marker"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="†", appears_before_marker="important")
|
||
|
||
# Use proper footnote format
|
||
content = """This is important[^†] here."""
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test when text doesn't appear before
|
||
content = """This is something[^†] else important."""
|
||
result, explanation = test.run(content)
|
||
self.assertFalse(result)
|
||
self.assertIn("not found before", explanation)
|
||
|
||
def test_special_symbols_with_appears_after(self):
|
||
"""Test special symbol markers with position checking"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="‡", appears_after_marker="critical finding")
|
||
|
||
# Test when text appears after the marker
|
||
content = "The reference<sup>‡</sup> critical finding was unexpected."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test when text doesn't appear after the marker
|
||
content = "The critical finding was before <sup>‡</sup> the marker."
|
||
result, explanation = test.run(content)
|
||
self.assertFalse(result)
|
||
self.assertIn("not found after", explanation)
|
||
|
||
def test_escaped_special_characters_in_regex(self):
|
||
"""Test that special regex characters in markers are properly escaped"""
|
||
# These characters have special meaning in regex and need escaping
|
||
regex_special_markers = ["*", "**", ".", "^", "$", "+", "?", "|", "\\", "(", ")", "[", "]", "{", "}"]
|
||
|
||
for marker in regex_special_markers:
|
||
try:
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker=marker)
|
||
|
||
# Test with markdown format
|
||
content = f"Text[^{marker}].\n\n[^{marker}]: Note."
|
||
result, _ = test.run(content)
|
||
# We expect this to work for valid markers like "*" and "**"
|
||
# But some like "\" might not be valid in markdown
|
||
|
||
# Test with HTML format which should work for all
|
||
content = f"Text<sup>{marker}</sup>."
|
||
result, _ = test.run(content)
|
||
|
||
except Exception as e:
|
||
# Some markers might not be valid, that's okay
|
||
pass
|
||
|
||
def test_mixed_formats_same_document(self):
|
||
"""Test document with mixed footnote formats"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="†")
|
||
|
||
# Document with both markdown and HTML footnotes
|
||
content = """
|
||
Some text[^†] with markdown format.
|
||
Other text<sup>†</sup> with HTML format.
|
||
|
||
[^†]: This is the footnote definition.
|
||
"""
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_double_dagger_and_similar(self):
|
||
"""Test markers that are repeated symbols"""
|
||
repeated_markers = ["**", "††", "‡‡", "§§", "***"]
|
||
|
||
for marker in repeated_markers:
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker=marker)
|
||
|
||
# Test markdown format
|
||
content = f"Text[^{marker}].\n\n[^{marker}]: Note text."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result, f"Failed for repeated marker '{marker}'")
|
||
|
||
def test_case_sensitive_word_markers(self):
|
||
"""Test that word markers are case-sensitive"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="Note")
|
||
|
||
# Should match exact case
|
||
content = "Text[^Note].\n\n[^Note]: Reference."
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Should not match different case
|
||
content = "Text[^note].\n\n[^note]: Reference."
|
||
result, _ = test.run(content)
|
||
self.assertFalse(result)
|
||
|
||
def test_html_footnote_definitions(self):
|
||
"""Test HTML-style footnote definitions"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="1")
|
||
|
||
# HTML style: <sup>1</sup> marker
|
||
content = """
|
||
Some text with a reference<sup>1</sup> in the middle.
|
||
|
||
At the bottom:
|
||
<sup>1</sup>This is the footnote text that appears right after the marker.
|
||
"""
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_html_footnote_with_special_symbols(self):
|
||
"""Test HTML-style footnotes with special symbol markers"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="†")
|
||
|
||
# HTML style with special symbol
|
||
content = """
|
||
Important finding<sup>†</sup> in the research.
|
||
|
||
<sup>†</sup>See appendix for details and supplementary materials.
|
||
"""
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_mixed_footnote_styles(self):
|
||
"""Test document with both markdown and HTML footnote definitions"""
|
||
# Test for first footnote (markdown style)
|
||
test1 = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="1")
|
||
|
||
content = """
|
||
First point[^1] and second point<sup>2</sup>.
|
||
|
||
[^1]: First reference in markdown style.
|
||
|
||
<sup>2</sup>Second reference in HTML style.
|
||
"""
|
||
result, _ = test1.run(content)
|
||
self.assertTrue(result)
|
||
|
||
# Test for second footnote (HTML style)
|
||
test2 = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="2")
|
||
result, _ = test2.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_html_footnote_multiline(self):
|
||
"""Test HTML footnote that spans multiple lines"""
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="3")
|
||
|
||
content = """
|
||
Reference here<sup>3</sup>.
|
||
|
||
<sup>3</sup>This footnote text
|
||
spans multiple lines
|
||
with more information.
|
||
"""
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
def test_real_world_case(self):
|
||
content = """## Abstract
|
||
|
||
Automated scientific discovery promises to accelerate progress across scientific domains. However, developing and evaluating an AI agent's capacity for end-to-end scientific reasoning is challenging as running real-world experiments is often prohibitively expensive or infeasible. In this work we introduce DISCOVERYWORLD, the first virtual environment for developing and benchmarking an agent's ability to perform complete cycles of novel scientific discovery. DISCOVERYWORLD contains a variety of different challenges, covering topics as diverse as radioisotope dating, rocket science, and proteomics, to encourage development of *general* discovery skills rather than task-specific solutions. DISCOVERYWORLD itself is an inexpensive, simulated, text-based environment (with optional 2D visual overlay). It includes 120 different challenge tasks, spanning eight topics each with three levels of difficulty and several parametric variations. Each task requires an agent to form hypotheses, design and run experiments, analyze results, and act on conclusions. DISCOVERYWORLD further provides three automatic metrics for evaluating performance, based on (a) task completion, (b) task-relevant actions taken, and (c) the discovered explanatory knowledge. We find that strong baseline agents, that perform well in prior published environments, struggle on most DISCOVERYWORLD tasks, suggesting that DISCOVERYWORLD captures some of the novel challenges of discovery, and thus that DISCOVERYWORLD may help accelerate near-term development and assessment of scientific discovery competency in agents. Code available at github.com/allenai/discoveryworld.<sup>1</sup>
|
||
|
||
## 1 Introduction
|
||
|
||
A long-standing dream of AI has been to build systems that can perform scientific discovery, potentially leading to new breakthroughs for the benefit of humanity [13]. Recently, with the rise of neural techniques, there have been several successful discovery systems developed for specialized problems such as protein folding [10, 15], mathematics [22], and material science [24]. However, while the results have been impressive, these systems (deliberately) bypass the full discovery process of ideation, hypothesis formation, experiment design, etc., and instead (expertly) perform systematic searches over a pre-defined hypothesis space, with pre-defined goals. This raises the question: how much more can be achieved if AI is applied to the broader scientific process? Some works have indeed developed early systems for this, for example, in chemistry [1], and genetics [12]. These systems can also generate hypotheses, design experiments, and execute them (via robotics) in real environments. However, operating in real environments is expensive and complex, creating a barrier
|
||
|
||
<sup>1</sup>Released under Apache-2.0 license."""
|
||
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="1")
|
||
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
test = FootnoteTest(
|
||
pdf="test.pdf",
|
||
page=1,
|
||
id="test_id",
|
||
type=TestType.FOOTNOTE.value,
|
||
marker="1",
|
||
appears_before_marker="Code available at github.com/allenai/discoveryworld.",
|
||
)
|
||
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
test = FootnoteTest(pdf="test.pdf", page=1, id="test_id", type=TestType.FOOTNOTE.value, marker="1", appears_after_marker="Released under Apache")
|
||
|
||
result, _ = test.run(content)
|
||
self.assertTrue(result)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
unittest.main()
|