Coverage for kea/kea_test.py: 86%

50 statements  

« prev     ^ index     » next       coverage.py v7.6.9, created at 2024-12-22 16:05 +0800

1import inspect 

2from typing import Callable, Any, Union, TYPE_CHECKING 

3from .kea import Rule, MainPath, Initializer 

4from .utils import PRECONDITIONS_MARKER, RULE_MARKER, INITIALIZER_MARKER, MAINPATH_MARKER 

5 

6if TYPE_CHECKING: 

7 from .android_pdl_driver import Android_PDL_Driver 

8 from .harmonyos_pdl_driver import HarmonyOS_PDL_Driver 

9 from .kea import Rule, MainPath 

10 

11class KeaTest: 

12 """Each app property to be tested inherits from KeaTest 

13 

14 In the future, app-agnostic properties (e.g., data loss detectors)  

15 can be implemented in KeaTest so that the Kea users can directly  

16 reuse these properties for functional validation. 

17 """ 

18 pass 

19 

20# `d` is the pdl driver for Android or HarmonyOS 

21d:Union["Android_PDL_Driver", "HarmonyOS_PDL_Driver", None] = None 

22 

23def rule() -> Callable: 

24 """the decorator @rule 

25 

26 A rule denotes an app property. 

27 """ 

28 def accept(f): 

29 precondition = getattr(f, PRECONDITIONS_MARKER, ()) 

30 rule = Rule(function=f, preconditions=precondition) 

31 

32 def rule_wrapper(*args, **kwargs): 

33 return f(*args, **kwargs) 

34 

35 setattr(rule_wrapper, RULE_MARKER, rule) 

36 return rule_wrapper 

37 

38 return accept 

39 

40 

41def precondition(precond: Callable[[Any], bool]) -> Callable: 

42 """the decorator @precondition 

43 

44 The precondition specifies when the property could be executed. 

45 A property could have multiple preconditions, each of which is specified by @precondition. 

46 """ 

47 def accept(f): 

48 def precondition_wrapper(*args, **kwargs): 

49 return f(*args, **kwargs) 

50 

51 rule:"Rule" = getattr(f, RULE_MARKER, None) 

52 if rule is not None: 

53 new_rule = rule.evolve(preconditions=rule.preconditions + (precond,)) 

54 # new_rule = attr.evolve(rule, preconditions=rule.preconditions + (precond,)) 

55 setattr(precondition_wrapper, RULE_MARKER, new_rule) 

56 else: 

57 setattr( 

58 precondition_wrapper, 

59 PRECONDITIONS_MARKER, 

60 getattr(f, PRECONDITIONS_MARKER, ()) + (precond,), 

61 ) 

62 return precondition_wrapper 

63 

64 return accept 

65 

66def initializer(): 

67 '''the decorator @initializer 

68 

69 An initialize decorator behaves like a rule, but all ``@initializer()`` decorated 

70 methods will be called before any ``@rule()`` decorated methods, in an arbitrary 

71 order. Each ``@initializer()`` method will be called exactly once per run, unless 

72 one raises an exception. 

73 ''' 

74 

75 def accept(f): 

76 def initialize_wrapper(*args, **kwargs): 

77 return f(*args, **kwargs) 

78 initializer_func = Initializer(function=f) 

79 #rule = Rule(function=f, preconditions=()) 

80 setattr(initialize_wrapper, INITIALIZER_MARKER, initializer_func) 

81 return initialize_wrapper 

82 

83 return accept 

84 

85def mainPath(): 

86 """the decorator @mainPath 

87 

88 A main path specifies a sequence of events which can lead to the UI state where  

89 the preconditions of a rule hold. 

90 """ 

91 def accept(f): 

92 def mainpath_wrapper(*args, **kwargs): 

93 source_code = inspect.getsource(f) 

94 code_lines = [line.strip() for line in source_code.splitlines() if line.strip()] 

95 code_lines = [line for line in code_lines if not line.startswith('def ') and not line.startswith('@') and not line.startswith('#')] 

96 return code_lines 

97 

98 main_path = MainPath(function=f, path=mainpath_wrapper()) 

99 setattr(mainpath_wrapper, MAINPATH_MARKER, main_path) 

100 return mainpath_wrapper 

101 

102 return accept