Coverage for src/bluetooth_sig/gatt/characteristics/utils/data_validator.py: 100%

44 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2025-10-30 00:10 +0000

1"""Data validation and integrity checking utilities.""" 

2 

3from __future__ import annotations 

4 

5from enum import IntEnum 

6 

7from ...constants import ( 

8 ABSOLUTE_ZERO_CELSIUS, 

9 EXTENDED_PERCENTAGE_MAX, 

10 MAX_CONCENTRATION_PPM, 

11 MAX_POWER_WATTS, 

12 MAX_TEMPERATURE_CELSIUS, 

13 PERCENTAGE_MAX, 

14 PERCENTAGE_MIN, 

15) 

16from ...exceptions import DataValidationError, EnumValueError, ValueRangeError 

17 

18 

19class DataValidator: 

20 """Utility class for data validation and integrity checking.""" 

21 

22 @staticmethod 

23 def validate_data_length(data: bytearray, expected_min: int, expected_max: int | None = None) -> None: 

24 """Validate data length against expected range.""" 

25 length = len(data) 

26 if length < expected_min: 

27 raise DataValidationError("data", data, f"at least {expected_min} bytes") 

28 if expected_max is not None and length > expected_max: 

29 raise DataValidationError("data_length", length, f"at most {expected_max} bytes") 

30 

31 @staticmethod 

32 def validate_range(value: int | float, min_val: int | float, max_val: int | float) -> None: 

33 """Validate that a value is within the specified range.""" 

34 if not min_val <= value <= max_val: 

35 raise ValueRangeError("value", value, min_val, max_val) 

36 

37 @staticmethod 

38 def validate_enum_value(value: int, enum_class: type[IntEnum]) -> None: 

39 """Validate that a value is a valid member of an IntEnum.""" 

40 try: 

41 enum_class(value) 

42 except ValueError as e: 

43 valid_values = [member.value for member in enum_class] 

44 raise EnumValueError(enum_class.__name__, value, enum_class, valid_values) from e 

45 

46 @staticmethod 

47 def validate_concentration_range(value: float, max_ppm: float = MAX_CONCENTRATION_PPM) -> None: 

48 """Validate concentration value is in acceptable range.""" 

49 if value < PERCENTAGE_MIN: 

50 raise ValueRangeError("concentration", value, 0, max_ppm) 

51 if value > max_ppm: 

52 raise ValueRangeError("concentration", value, 0, max_ppm) 

53 

54 @staticmethod 

55 def validate_temperature_range( 

56 value: float, 

57 min_celsius: float = ABSOLUTE_ZERO_CELSIUS, 

58 max_celsius: float = MAX_TEMPERATURE_CELSIUS, 

59 ) -> None: 

60 """Validate temperature is in physically reasonable range.""" 

61 if value < min_celsius: 

62 raise ValueRangeError("temperature", value, min_celsius, max_celsius) 

63 if value > max_celsius: 

64 raise ValueRangeError("temperature", value, min_celsius, max_celsius) 

65 

66 @staticmethod 

67 def validate_percentage(value: int | float, allow_over_100: bool = False) -> None: 

68 """Validate percentage value (0-100% or 0-200% for some characteristics).""" 

69 max_value = EXTENDED_PERCENTAGE_MAX if allow_over_100 else PERCENTAGE_MAX 

70 if value < PERCENTAGE_MIN or value > max_value: 

71 raise ValueRangeError("percentage", value, 0, max_value) 

72 

73 @staticmethod 

74 def validate_power_range(value: int | float, max_watts: float = MAX_POWER_WATTS) -> None: 

75 """Validate power measurement range.""" 

76 if value < 0 or value > max_watts: 

77 raise ValueRangeError("power", value, 0, max_watts)