mirror of
https://github.com/BerriAI/litellm.git
synced 2026-09-10 22:41:41 +00:00
* wip * refactor(tests): move sdk function tracing into rust python harness * dead code * fix: handle harness keyboard interrupts * refactor(tests): deduplicate rust python harness helpers * fix(harness): expose validated strategy choices * wip * refactor(harness): let strategies own parity reports * docs(harness): update strategy structure * refactor(harness): localize strategy report views * wip * fix(harness): satisfy mapping runner type checks * fix(harness): clarify trace parity output * wip * fix(harness): clarify unit mapping report * fix(harness): finalize trace parity contracts * refactor(harness): structure parity contracts * feat: derive unit test mapping from traces * feat(harness): map rstest test families * feat(ocr): port Azure document intelligence tests * feat(harness): enforce complete unit mappings * feat(ocr): add reducto core transforms * feat(harness): classify host-only unit tests * fix(ocr): complete Rust provider plumbing * fix(harness): reuse OCR parity workers
220 lines
8.2 KiB
Python
220 lines
8.2 KiB
Python
from __future__ import annotations
|
|
|
|
from collections import Counter
|
|
from typing import Final, Literal
|
|
|
|
from pydantic import BaseModel, ConfigDict, field_validator, model_validator
|
|
from typing_extensions import Self
|
|
|
|
from ...shared.tracing.pytest_usage import PythonFunctionReference
|
|
from ...shared.unit_runners.rust_runner import RustTarget, RustTestIdentity, RustTestScope
|
|
|
|
|
|
class _ContractModel(BaseModel):
|
|
model_config = ConfigDict(frozen=True, extra="forbid")
|
|
|
|
|
|
def _clean_unique(values: tuple[str, ...], field: str) -> tuple[str, ...]:
|
|
cleaned: Final = tuple(value.strip().rstrip("/") for value in values)
|
|
if not cleaned or any(not value for value in cleaned):
|
|
raise ValueError(f"{field} must contain non-empty paths")
|
|
duplicates: Final = tuple(value for value, count in Counter(cleaned).items() if count > 1)
|
|
if duplicates:
|
|
raise ValueError(f"{field} contains duplicates: {sorted(duplicates)}")
|
|
return cleaned
|
|
|
|
|
|
def _selector_contains(parent: str, child: str) -> bool:
|
|
return child == parent or child.startswith(f"{parent}/")
|
|
|
|
|
|
class RustTestFamily(_ContractModel):
|
|
kind: Literal["family"] = "family"
|
|
target: RustTarget
|
|
name: str
|
|
|
|
@field_validator("name")
|
|
@classmethod
|
|
def validate_name(cls, value: str) -> str:
|
|
stripped: Final = value.strip()
|
|
if not stripped or stripped.endswith("::"):
|
|
raise ValueError("must be a non-empty Rust test base name")
|
|
return stripped
|
|
|
|
@property
|
|
def key(self) -> str:
|
|
return f"{self.target.key}::{self.name}::case_*"
|
|
|
|
def contains(self, identity: RustTestIdentity) -> bool:
|
|
return identity.target == self.target and identity.name.startswith(f"{self.name}::case_")
|
|
|
|
|
|
class TestMapping(_ContractModel):
|
|
python: str
|
|
rust: RustTestIdentity | RustTestFamily
|
|
|
|
@field_validator("python")
|
|
@classmethod
|
|
def validate_python_nodeid(cls, value: str) -> str:
|
|
stripped: Final = value.strip()
|
|
if "::" not in stripped:
|
|
raise ValueError("must be a source path and test name separated by '::'")
|
|
return stripped
|
|
|
|
|
|
class PythonFunctionDiscoverySpec(_ContractModel):
|
|
functions: tuple[PythonFunctionReference, ...] = ()
|
|
trace_module: str | None = None
|
|
trace_spans: tuple[str, ...] = ()
|
|
search_roots: tuple[str, ...]
|
|
exclude_roots: tuple[str, ...] = ()
|
|
includes: tuple[str, ...] = ()
|
|
exclusions: tuple[str, ...] = ()
|
|
|
|
@field_validator("search_roots")
|
|
@classmethod
|
|
def validate_search_roots(cls, value: tuple[str, ...]) -> tuple[str, ...]:
|
|
return _clean_unique(value, "python function search_roots")
|
|
|
|
@field_validator("exclude_roots")
|
|
@classmethod
|
|
def validate_exclude_roots(cls, value: tuple[str, ...]) -> tuple[str, ...]:
|
|
if not value:
|
|
return ()
|
|
return _clean_unique(value, "python function exclude_roots")
|
|
|
|
@model_validator(mode="after")
|
|
def validate_functions(self) -> Self:
|
|
if bool(self.functions) == bool(self.trace_module):
|
|
raise ValueError("python function discovery needs exactly one function list or trace module")
|
|
if self.trace_module is not None and not self.trace_spans:
|
|
raise ValueError("trace-derived Python function discovery needs trace_spans")
|
|
if not self.functions:
|
|
return self
|
|
keys: Final = tuple(f"{function.module}:{function.qualname}" for function in self.functions)
|
|
duplicates: Final = tuple(key for key, count in Counter(keys).items() if count > 1)
|
|
if duplicates:
|
|
raise ValueError(f"python function discovery contains duplicates: {sorted(duplicates)}")
|
|
return self
|
|
|
|
|
|
class UnitParityExclusionSpec(_ContractModel):
|
|
nodeid: str
|
|
reason: str
|
|
|
|
@field_validator("nodeid", "reason")
|
|
@classmethod
|
|
def validate_fields(cls, value: str) -> str:
|
|
stripped: Final = value.strip()
|
|
if not stripped:
|
|
raise ValueError("must be a non-empty string")
|
|
return stripped
|
|
|
|
|
|
class MappingExclusionSpec(_ContractModel):
|
|
nodeid: str
|
|
reason: str
|
|
|
|
@field_validator("nodeid", "reason")
|
|
@classmethod
|
|
def validate_fields(cls, value: str) -> str:
|
|
stripped: Final = value.strip()
|
|
if not stripped:
|
|
raise ValueError("must be a non-empty string")
|
|
return stripped
|
|
|
|
|
|
class MappingSpec(_ContractModel):
|
|
python_selectors: tuple[str, ...] = ()
|
|
python_functions: PythonFunctionDiscoverySpec | None = None
|
|
rust_scope: tuple[RustTestScope, ...] = ()
|
|
rust_targets: tuple[RustTarget, ...] = ()
|
|
mappings: tuple[TestMapping, ...]
|
|
exclusions: tuple[MappingExclusionSpec, ...] = ()
|
|
require_complete: bool = False
|
|
|
|
@field_validator("python_selectors")
|
|
@classmethod
|
|
def validate_python_selectors(cls, value: tuple[str, ...]) -> tuple[str, ...]:
|
|
if not value:
|
|
return ()
|
|
return _clean_unique(value, "python_selectors")
|
|
|
|
@model_validator(mode="after")
|
|
def validate_rust_scope(self) -> Self:
|
|
if bool(self.python_selectors) == bool(self.python_functions):
|
|
raise ValueError("mapping needs exactly one Python selector or function-discovery scope")
|
|
targets: Final = tuple(scope.target.key for scope in self.rust_scope)
|
|
duplicates: Final = tuple(target for target, count in Counter(targets).items() if count > 1)
|
|
if duplicates:
|
|
raise ValueError(f"rust_scope contains duplicate targets: {sorted(duplicates)}")
|
|
target_names: Final = tuple(target.name for target in self.rust_targets)
|
|
duplicate_names: Final = tuple(name for name, count in Counter(target_names).items() if count > 1)
|
|
if duplicate_names:
|
|
raise ValueError(f"rust_targets contains duplicate names: {sorted(duplicate_names)}")
|
|
exclusion_nodeids: Final = tuple(exclusion.nodeid for exclusion in self.exclusions)
|
|
duplicate_exclusions: Final = tuple(nodeid for nodeid, count in Counter(exclusion_nodeids).items() if count > 1)
|
|
if duplicate_exclusions:
|
|
raise ValueError(f"mapping exclusions contain duplicate nodeids: {sorted(duplicate_exclusions)}")
|
|
return self
|
|
|
|
|
|
class UnitParitySpec(_ContractModel):
|
|
python_selectors: tuple[str, ...]
|
|
exclusions: tuple[UnitParityExclusionSpec, ...] = ()
|
|
|
|
@field_validator("python_selectors")
|
|
@classmethod
|
|
def validate_python_selectors(cls, value: tuple[str, ...]) -> tuple[str, ...]:
|
|
return _clean_unique(value, "unit parity python_selectors")
|
|
|
|
@model_validator(mode="after")
|
|
def validate_exclusions(self) -> Self:
|
|
nodeids: Final = tuple(exclusion.nodeid for exclusion in self.exclusions)
|
|
duplicates: Final = tuple(nodeid for nodeid, count in Counter(nodeids).items() if count > 1)
|
|
if duplicates:
|
|
raise ValueError(f"unit parity exclusions contain duplicate nodeids: {sorted(duplicates)}")
|
|
return self
|
|
|
|
|
|
class RustUnitSpec(_ContractModel):
|
|
cargo_manifest: str
|
|
cargo_filter: str
|
|
cargo_package: str | None = None
|
|
|
|
@field_validator("cargo_manifest", "cargo_filter")
|
|
@classmethod
|
|
def validate_required_fields(cls, value: str) -> str:
|
|
stripped: Final = value.strip()
|
|
if not stripped:
|
|
raise ValueError("must be a non-empty string")
|
|
return stripped
|
|
|
|
@field_validator("cargo_package")
|
|
@classmethod
|
|
def validate_package(cls, value: str | None) -> str | None:
|
|
if value is None:
|
|
return None
|
|
stripped: Final = value.strip()
|
|
if not stripped:
|
|
raise ValueError("must be a non-empty string when provided")
|
|
return stripped
|
|
|
|
|
|
class UnitTestContract(_ContractModel):
|
|
mapping: MappingSpec
|
|
unit_parity: UnitParitySpec
|
|
rust: RustUnitSpec
|
|
|
|
@model_validator(mode="after")
|
|
def validate_unit_parity_scope(self) -> Self:
|
|
if not self.mapping.python_selectors:
|
|
return self
|
|
unknown: Final = tuple(
|
|
selector
|
|
for selector in self.unit_parity.python_selectors
|
|
if not any(_selector_contains(parent, selector) for parent in self.mapping.python_selectors)
|
|
)
|
|
if unknown:
|
|
raise ValueError(f"unit parity selectors must be contained in mapping selectors: {sorted(unknown)}")
|
|
return self
|