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
« 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
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
11class KeaTest:
12 """Each app property to be tested inherits from KeaTest
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
20# `d` is the pdl driver for Android or HarmonyOS
21d:Union["Android_PDL_Driver", "HarmonyOS_PDL_Driver", None] = None
23def rule() -> Callable:
24 """the decorator @rule
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)
32 def rule_wrapper(*args, **kwargs):
33 return f(*args, **kwargs)
35 setattr(rule_wrapper, RULE_MARKER, rule)
36 return rule_wrapper
38 return accept
41def precondition(precond: Callable[[Any], bool]) -> Callable:
42 """the decorator @precondition
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)
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
64 return accept
66def initializer():
67 '''the decorator @initializer
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 '''
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
83 return accept
85def mainPath():
86 """the decorator @mainPath
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
98 main_path = MainPath(function=f, path=mainpath_wrapper())
99 setattr(mainpath_wrapper, MAINPATH_MARKER, main_path)
100 return mainpath_wrapper
102 return accept