[PATCH 13/23] rteval MCP: Add integration tests and remove hardcoded examples
John Kacur <[email protected]>
| Newsgroups | org.kernel.vger.linux-rt-users |
|---|---|
| Message-ID | <[email protected]> |
Replace hardcoded example scripts with a proper integration test that validates MCP server tools through the actual MCP interface. Removed files (had hardcoded paths to deleted test data): - test_server.py: Basic sanity check, too minimal - test_comparison.py: Redundant with compare_results tool - test_logs.py: Redundant with list_logs/read_log tools - test_tools.py: Replaced by test_mcp_integration.py Added file: - tests/test_mcp_integration.py: End-to-end integration test - Tests 8 MCP tools through the MCP interface - Dynamically finds available rteval result directories - No hardcoded paths - works with any rteval data - Works from rteval/, rteval/mcp-server/, or rteval/mcp-server/tests/ - Discovered bugs in filter_results, find_best_worst, compare_to_baseline Test coverage: - list_tools, list_results, parse_result, list_logs - get_percentiles, extract_histogram (new histogram features) - filter_results, find_best_worst (query/filter tools) All tests pass after server.py glob pattern fixes. Assisted-by: Claude:claude-sonnet-4-5 Signed-off-by: John Kacur <[email protected]> --- mcp-server/tests/test_mcp_integration.py | 195 +++++++++++++++++++++++ 1 file changed, 195 insertions(+) create mode 100644 mcp-server/tests/test_mcp_integration.py diff --git a/mcp-server/tests/test_mcp_integration.py b/mcp-server/tests/test_mcp_integration.py new file mode 100644 index 000000000000..30f7700786fa --- /dev/null +++ b/mcp-server/tests/test_mcp_integration.py @@ -0,0 +1,195 @@ +#!/usr/bin/env python3 +"""Integration tests for rteval MCP server. + +Tests the MCP server tools through the actual MCP interface, +validating end-to-end functionality. +""" + +import sys +import asyncio +from pathlib import Path + +# Add parent directory to path to import server module +sys.path.insert(0, str(Path(__file__).parent.parent)) +from server import call_tool, list_tools + + +def find_test_data(): + """Find available rteval result directories for testing.""" + # From tests/ -> mcp-server/ -> rteval/ + rteval_dir = Path(__file__).parent.parent.parent + + # Find rteval result directories (not rteval-build) + result_dirs = sorted([ + d for d in rteval_dir.glob("rteval-*/") + if d.is_dir() and d.name != "rteval-build" and (d / "summary.xml").exists() + ]) + + if not result_dirs: + raise RuntimeError("No rteval result directories found for testing") + + return result_dirs + + +async def main(): + print("MCP Server Integration Tests") + print("=" * 60) + + # Find test data + try: + result_dirs = find_test_data() + test_dir = result_dirs[0] + test_file = test_dir / "summary.xml" + rteval_parent = test_dir.parent + + print(f"\nUsing test data: {test_dir.name}") + print(f"Total result directories available: {len(result_dirs)}") + print() + except RuntimeError as e: + print(f"✗ {e}") + sys.exit(1) + + # Test 1: List available tools + print("1. Testing list_tools()...") + try: + tools = await list_tools() + print(f"✓ Found {len(tools)} tools:") + for tool in tools: + print(f" - {tool.name}: {tool.description[:60]}...") + except Exception as e: + print(f"✗ Failed: {e}") + sys.exit(1) + + # Test 2: List results + print("\n2. Testing list_results...") + try: + result = await call_tool("list_results", { + "directory": str(rteval_parent), + "pattern": "rteval-*/summary.xml" + }) + output = result[0].text + if "Found" in output and "file" in output: + lines = output.split('\n')[:5] + print("✓ " + '\n '.join(lines)) + else: + print(f"✗ Unexpected output: {output[:200]}") + sys.exit(1) + except Exception as e: + print(f"✗ Failed: {e}") + sys.exit(1) + + # Test 3: Parse a result + print("\n3. Testing parse_result...") + try: + result = await call_tool("parse_result", { + "file_path": str(test_file) + }) + output = result[0].text + if "rteval version" in output and "System Information" in output: + lines = output.split('\n')[:10] + print("✓ " + '\n '.join(lines)) + else: + print(f"✗ Unexpected output: {output[:200]}") + sys.exit(1) + except Exception as e: + print(f"✗ Failed: {e}") + sys.exit(1) + + # Test 4: List logs + print("\n4. Testing list_logs...") + try: + result = await call_tool("list_logs", { + "result_dir": str(test_dir) + }) + output = result[0].text + if "Log files" in output or "logs" in output.lower(): + lines = output.split('\n')[:8] + print("✓ " + '\n '.join(lines)) + else: + print(f"✗ Unexpected output: {output[:200]}") + sys.exit(1) + except Exception as e: + print(f"✗ Failed: {e}") + sys.exit(1) + + # Test 5: Get percentiles (new histogram feature) + print("\n5. Testing get_percentiles...") + try: + result = await call_tool("get_percentiles", { + "file_path": str(test_file), + "percentiles": [50, 95, 99, 99.9] + }) + output = result[0].text + if "Percentiles" in output and "P50" in output: + lines = output.split('\n')[:12] + print("✓ " + '\n '.join(lines)) + else: + print(f"✗ Unexpected output: {output[:200]}") + sys.exit(1) + except Exception as e: + print(f"✗ Failed: {e}") + sys.exit(1) + + # Test 6: Extract histogram (new feature) + print("\n6. Testing extract_histogram...") + try: + result = await call_tool("extract_histogram", { + "file_path": str(test_file), + "include_per_cpu": False # Keep output brief + }) + output = result[0].text + if "Histogram Data" in output and "Total Samples" in output: + lines = output.split('\n')[:12] + print("✓ " + '\n '.join(lines)) + else: + print(f"✗ Unexpected output: {output[:200]}") + sys.exit(1) + except Exception as e: + print(f"✗ Failed: {e}") + sys.exit(1) + + # Test 7: Filter results (query tools) + print("\n7. Testing filter_results...") + try: + result = await call_tool("filter_results", { + "directory": str(rteval_parent), + "is_rt": False + }) + output = result[0].text + if "Filtered Results" in output or "files matched" in output: + lines = output.split('\n')[:8] + print("✓ " + '\n '.join(lines)) + else: + print(f"✗ Unexpected output: {output[:200]}") + sys.exit(1) + except Exception as e: + print(f"✗ Failed: {e}") + sys.exit(1) + + # Test 8: Find best/worst + print("\n8. Testing find_best_worst...") + try: + result = await call_tool("find_best_worst", { + "directory": str(rteval_parent), + "metric": "maximum", + "count": 3 + }) + output = result[0].text + if ("BEST" in output and "WORST" in output) or "No results" in output: + lines = output.split('\n')[:10] + print("✓ " + '\n '.join(lines)) + else: + print(f"✗ Unexpected output: {output[:200]}") + sys.exit(1) + except Exception as e: + print(f"✗ Failed: {e}") + sys.exit(1) + + print("\n" + "=" * 60) + print("✓ All MCP server integration tests passed!") + print(f"\nTested with: {test_dir.name}") + print(f"Total tools tested: 8") + + +if __name__ == "__main__": + asyncio.run(main()) -- 2.55.0