""" Performance tests for citation formatter to ensure it handles large documents efficiently. """ import time import pytest from local_deep_research.text_optimization.citation_formatter import ( CitationFormatter, CitationMode, ) class TestCitationPerformance: """Test performance of citation formatter with large documents.""" def test_many_citations_performance(self): """Test performance with many citations.""" # Generate content with 1000 citations citations = [] sources = [] for i in range(1, 1001): # Mix up citation patterns if i % 3 == 0: citations.append(f"Multiple [{i}] citations [{i}] here [{i}]") else: citations.append(f"Single citation [{i}]") sources.append(f"""[{i}] Source {i} Title URL: https://example{i % 100}.com/paper/{i}""") content = f"""# Large Document Performance Test {" ".join(citations)} ## Sources {chr(10).join(sources)}""" formatter = CitationFormatter(mode=CitationMode.DOMAIN_HYPERLINKS) start_time = time.time() result = formatter.format_document(content) end_time = time.time() processing_time = end_time - start_time # Should complete in reasonable time (less than 5 seconds for 1000 citations) assert processing_time < 5.0, ( f"Processing took {processing_time:.2f}s, which is too slow" ) # Should have formatted all citations for i in range(100): assert f"[example{i}.com]" in result def test_long_document_performance(self): """Test performance with very long documents.""" # Create a 100KB document paragraphs = [] for i in range(200): paragraph = f""" This is paragraph {i} with some citations [{i % 50 + 1}] and more text to make it longer. The citation [{i % 50 + 1}] appears multiple times in this paragraph. Sometimes we reference [{(i + 1) % 50 + 1}] as well. This helps test performance with realistic document sizes and citation patterns. """ paragraphs.append(paragraph) # Add sources sources = [] for i in range(1, 51): sources.append(f"""[{i}] Academic Paper {i} URL: https://journal{i % 10}.com/article/{i}""") content = f"""# Very Long Document {"".join(paragraphs)} ## Sources {chr(10).join(sources)}""" formatter = CitationFormatter(mode=CitationMode.DOMAIN_ID_HYPERLINKS) start_time = time.time() result = formatter.format_document(content) end_time = time.time() processing_time = end_time - start_time # Should handle large documents efficiently assert processing_time < 10.0, ( f"Processing took {processing_time:.2f}s for large document" ) # Verify formatting worked assert "[journal0.com" in result assert "[journal9.com" in result def test_deeply_nested_markdown_performance(self): """Test performance with deeply nested markdown structures.""" # Create nested lists with citations nested_content = """# Document with Nested Structure """ for i in range(10): nested_content += f"{' ' * i}- Level {i} item [{i + 1}]\n" nested_content += ( f"{' ' * i} - Sub-item with citation [{i + 11}]\n" ) nested_content += f"{' ' * i} - Deep sub-item [{i + 21}]\n" # Add complex markdown nested_content += """ | Header 1 [31] | Header 2 [32] | Header 3 [33] | |---------------|---------------|---------------| | Cell [34] | Cell [35] | Cell [36] | | Cell [37] | Cell [38] | Cell [39] | > Blockquote level 1 [40] >> Blockquote level 2 [41] >>> Blockquote level 3 [42] """ # Add sources sources = [] for i in range(1, 43): sources.append( f"[{i}] Source {i}\nURL: https://site{i % 5}.com/{i}" ) content = nested_content + "\n## Sources\n\n" + "\n\n".join(sources) formatter = CitationFormatter(mode=CitationMode.DOMAIN_HYPERLINKS) start_time = time.time() result = formatter.format_document(content) end_time = time.time() processing_time = end_time - start_time # Should handle complex structure efficiently assert processing_time < 2.0, ( f"Processing complex structure took {processing_time:.2f}s" ) # Verify citations in various contexts were formatted assert "[site0.com]" in result assert "[site4.com]" in result def test_repeated_formatting_performance(self): """Test performance when formatting the same document multiple times.""" content = """# Document Citations [1] and [2] and [3]. ## Sources [1] First URL: https://first.com [2] Second URL: https://second.com [3] Third URL: https://third.com""" formatter = CitationFormatter(mode=CitationMode.DOMAIN_HYPERLINKS) # Format 100 times start_time = time.time() for _ in range(100): formatter.format_document(content) end_time = time.time() total_time = end_time - start_time avg_time = total_time / 100 # Average time should be very fast assert avg_time < 0.01, ( f"Average formatting time {avg_time:.4f}s is too slow" ) def test_citation_modes_performance_comparison(self): """Compare performance across different citation modes.""" # Create test document citations = [f"Citation [{i}]" for i in range(1, 101)] sources = [ f"[{i}] Source {i}\nURL: https://domain{i % 20}.com/{i}" for i in range(1, 101) ] content = f"""# Performance Test {" ".join(citations)} ## Sources {chr(10).join(sources)}""" modes = [ CitationMode.NUMBER_HYPERLINKS, CitationMode.DOMAIN_HYPERLINKS, CitationMode.DOMAIN_ID_HYPERLINKS, CitationMode.DOMAIN_ID_ALWAYS_HYPERLINKS, CitationMode.NO_HYPERLINKS, ] times = {} for mode in modes: formatter = CitationFormatter(mode=mode) start_time = time.time() formatter.format_document(content) end_time = time.time() times[mode.value] = end_time - start_time # All modes should complete quickly for mode_name, duration in times.items(): assert duration < 1.0, f"Mode {mode_name} took {duration:.3f}s" # Performance shouldn't vary drastically between modes # Exclude NO_HYPERLINKS from comparison as it does minimal processing times_without_no_hyperlinks = { k: v for k, v in times.items() if k != "no_hyperlinks" } if times_without_no_hyperlinks: max_time = max(times_without_no_hyperlinks.values()) min_time = min(times_without_no_hyperlinks.values()) # Allow more variance as some modes do more work if min_time > 0: assert max_time / min_time < 10.0, ( f"Performance varies too much between modes (excluding no_hyperlinks): {times_without_no_hyperlinks}" ) # Verify NO_HYPERLINKS is indeed the fastest if "no_hyperlinks" in times: no_hyperlinks_time = times["no_hyperlinks"] other_times = [v for k, v in times.items() if k != "no_hyperlinks"] if other_times: # NO_HYPERLINKS should be faster or comparable to the fastest other mode assert no_hyperlinks_time <= min(other_times) * 2, ( "NO_HYPERLINKS mode should be among the fastest modes" ) def test_memory_efficiency_large_content(self): """Test that formatter doesn't use excessive memory.""" # Create a large document (10MB+) large_text = "This is a test paragraph. " * 10000 content = f"""# Large Document {large_text} Citation at the end [1]. ## Sources [1] Only Source URL: https://example.com/source""" formatter = CitationFormatter(mode=CitationMode.DOMAIN_HYPERLINKS) # Should handle large content without issues try: start_time = time.time() result = formatter.format_document(content) end_time = time.time() # Should complete even with large content assert "[example.com]" in result assert end_time - start_time < 5.0 except MemoryError: pytest.fail("Formatter used too much memory") @pytest.mark.parametrize("num_sources", [10, 50, 100, 500]) def test_scaling_with_source_count(self, num_sources): """Test how performance scales with number of sources.""" # Create content with varying number of sources citations = [] sources = [] # Each source cited 3 times for i in range(1, num_sources + 1): citations.extend([f"[{i}]", f"[{i}]", f"[{i}]"]) sources.append(f"[{i}] Source {i}\nURL: https://example.com/{i}") content = f"""# Scaling Test Citations: {" ".join(citations)} ## Sources {chr(10).join(sources)}""" formatter = CitationFormatter(mode=CitationMode.DOMAIN_ID_HYPERLINKS) start_time = time.time() formatter.format_document(content) end_time = time.time() processing_time = end_time - start_time # Time should scale roughly linearly with source count expected_max_time = num_sources * 0.01 # 10ms per source max assert processing_time < expected_max_time, ( f"Processing {num_sources} sources took {processing_time:.3f}s, " f"expected < {expected_max_time:.3f}s" ) def test_regex_performance_edge_cases(self): """Test performance with content that might cause regex issues.""" # Create content with patterns that could cause backtracking tricky_patterns = [ "[" * 100 + "1" + "]" * 100, # Many brackets "[[[[1]]]]", # Nested brackets "[1][2][3][4][5]" * 50, # Many consecutive citations "[1] " + "." * 1000 + " [2]", # Long spans between citations ] sources = """ [1] Source 1 URL: https://one.com [2] Source 2 URL: https://two.com [3] Source 3 URL: https://three.com [4] Source 4 URL: https://four.com [5] Source 5 URL: https://five.com""" for pattern in tricky_patterns: content = f"# Test\n\n{pattern}\n\n## Sources\n{sources}" formatter = CitationFormatter(mode=CitationMode.DOMAIN_HYPERLINKS) start_time = time.time() try: formatter.format_document(content) end_time = time.time() # Should complete quickly even with tricky patterns assert end_time - start_time < 1.0 except Exception as e: pytest.fail(f"Failed on pattern: {pattern[:50]}... Error: {e}")