1+ """
2+ Pytest plugin that dynamically marks static tests based on their tag compatibility.
3+
4+ This plugin examines static test files and marks them as either:
5+ - `tagged`: Tests that use the tag system (compatible)
6+ - `untagged`: Tests that have hardcoded addresses (incompatible)
7+ """
8+
9+ import json
10+ import re
11+ from pathlib import Path
12+ from typing import Any , Dict , Set
13+
14+ import pytest
15+ import yaml
16+
17+ from pytest_plugins .filler .static_filler import NoIntResolver
18+
19+
20+ def has_tags_in_content (content : Dict [str , Any ]) -> bool :
21+ """Check if test content uses tags for addresses."""
22+ tag_pattern = re .compile (r"<(?:eoa|contract|coinbase)(?::[^:]+)?:[^>]+>" )
23+
24+ def check_dict (d : Dict [str , Any ]) -> bool :
25+ """Recursively check dictionary for tags."""
26+ for key , value in d .items ():
27+ if isinstance (value , str ) and tag_pattern .search (value ):
28+ return True
29+ elif isinstance (value , dict ):
30+ if check_dict (value ):
31+ return True
32+ elif isinstance (value , list ):
33+ for item in value :
34+ if isinstance (item , dict ) and check_dict (item ):
35+ return True
36+ elif isinstance (item , str ) and tag_pattern .search (item ):
37+ return True
38+ return False
39+
40+ return check_dict (content )
41+
42+
43+ def pytest_collection_modifyitems (config , items ):
44+ """Mark tests based on their tag usage."""
45+ # Only process if static tests are being filled
46+ if not config .getoption ("fill_static_tests_enabled" ):
47+ return
48+
49+ # Track statistics
50+ tagged_count = 0
51+ untagged_count = 0
52+
53+ # Group items by their source file
54+ items_by_file = {}
55+ for item in items :
56+ if hasattr (item , "parent" ) and hasattr (item .parent , "path" ):
57+ file_path = item .parent .path
58+ if file_path not in items_by_file :
59+ items_by_file [file_path ] = []
60+ items_by_file [file_path ].append (item )
61+
62+ # Process each file once
63+ for file_path , file_items in items_by_file .items ():
64+ if not file_path .suffix in (".json" , ".yml" ):
65+ continue
66+
67+ if not file_path .stem .endswith ("Filler" ):
68+ continue
69+
70+ with open (file_path , "r" ) as f :
71+ file_content = (
72+ json .load (f )
73+ if file_path .suffix == ".json"
74+ else yaml .load (f , Loader = NoIntResolver )
75+ )
76+
77+ # For each test in the file
78+ for test_name , test_content in file_content .items ():
79+ # Check if the test uses tags
80+ uses_tags = has_tags_in_content (test_content )
81+
82+ # Find items that belong to this test
83+ test_items = [item for item in file_items if test_name in item .name ]
84+
85+ # Mark items for this test
86+ for item in test_items :
87+ if uses_tags :
88+ item .add_marker (pytest .mark .tagged )
89+ tagged_count += 1
90+ else :
91+ item .add_marker (pytest .mark .untagged )
92+ untagged_count += 1
93+
94+ # Report statistics if verbose
95+ if config .getoption ("verbose" ) >= 1 :
96+ print (f"\n Static test tag statistics:" )
97+ print (f" Tagged (compatible): { tagged_count } " )
98+ print (f" Untagged (incompatible): { untagged_count } " )
99+
100+
101+ def pytest_configure (config ):
102+ """Register markers."""
103+ config .addinivalue_line (
104+ "markers" , "tagged: marks tests that use the tag system"
105+ )
106+ config .addinivalue_line (
107+ "markers" , "untagged: marks tests that have hardcoded addresses"
108+ )
0 commit comments