annotate COBRAxy/testing.py @ 178:824b1ecc29db draft

Uploaded
author francesco_lapi
date Wed, 20 Nov 2024 14:59:42 +0000
parents 41f35c2f0c7b
children
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
4
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
1 # This is a general-purpose "testing utilities" module for the COBRAxy tool.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
2 # This code was written entirely by m.ferrari133@campus.unimib.it and then (hopefully) many
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
3 # more people contributed by writing tests for this tool's modules, feel free to send an email for
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
4 # any questions.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
5
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
6 # How the testing module works:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
7 # The testing module allows you to easily set up unit tests for functions in a module, obtaining
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
8 # information on what each method returns, when and how it fails and so on.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
9
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
10 # How do I test a module?
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
11 # - create a function at the very bottom, before the __main__
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
12 # - import the stuff you need
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
13 # - create a UnitTester instance, follow the documentation
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
14 # - fill it up with UnitTest instances, follow the documentation
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
15 # - each UnitTest tests the function by passing specific parameters to it and by veryfing the correctness
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
16 # of the output via a CheckingMode instance
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
17 # - call testModule() on the UnitTester
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
18
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
19 # TODO(s):
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
20 # - This module was written before the utilities were introduced, it may want to use some of those functions.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
21 # - I never got around to writing a CheckingMode for methods you WANT to fail in certain scenarios, I
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
22 # like the name "MustPanic".
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
23 # - It's good practice to enforce boolean arguments of a function to be passed as kwargs and I did it a lot
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
24 # in the code I wrote for these tool's modules, but the current implementation of UnitTest doesn't allow
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
25 # you to pass kwargs to the functions you test.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
26 # - Implement integration tests as well, maybe!
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
27
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
28 ## Imports:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
29 from typing import Dict, Callable, Type, List
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
30 from enum import Enum, auto
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
31 from collections.abc import Iterable
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
32
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
33 ## Generic utilities:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
34 class TestResult:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
35 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
36 Represents the result of a test and contains all the relevant information about it. Loosely models two variants:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
37 - Ok: The test passed, no further information is saved besides the target's name.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
38 - Err: The test failed, an error message and further contextual details are also saved.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
39
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
40 This class does not ensure a static proof of the two states' behaviour, their meaning or mutual exclusivity outside
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
41 of the :bool property "isPass", meant for outside reads.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
42 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
43 def __init__(self, isPass :bool, targetName :str, errMsg = "", details = "") -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
44 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
45 (Private) Initializes an instance of TestResult.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
46
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
47 Args:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
48 isPass : distinction between TestResult.Ok (True) and TestResult.Err (False).
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
49 targetName : the name of the target object / property / function / module being tested, not always set
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
50 to a meaningful value at this stage.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
51
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
52 errMsg : concise error message explaining the test's failure.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
53 details : contextual details about the error.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
54
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
55 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
56 None : practically, a TestResult instance.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
57 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
58 self.isPass = isPass
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
59 self.isFail = not isPass # Convenience above all
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
60
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
61 self.targetName = targetName
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
62 if isPass: return
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
63
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
64 self.errMsg = errMsg
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
65 self.details = details
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
66
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
67 @classmethod
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
68 def Ok(cls, targetName = "") -> "TestResult":
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
69 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
70 Factory method for TestResult.Ok, where all we need to know is that our test passed.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
71
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
72 Args:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
73 targetName : the name of the target object / property / function / module being tested, not always set
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
74 to a meaningful value at this stage.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
75
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
76 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
77 TestResult : a new Ok instance.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
78 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
79 return cls(True, targetName)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
80
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
81 @classmethod
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
82 def Err(cls, errMsg :str, details :str, targetName = "") -> "TestResult":
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
83 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
84 Factory method for TestResult.Err, where we store relevant error information.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
85
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
86 Args:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
87 errMsg : concise error message explaining the test's failure.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
88 details : contextual details about the error.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
89 targetName : the name of the target object / property / function / module being tested, not always set
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
90 to a meaningful value at this stage.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
91
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
92 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
93 TestResult : a new Err instance.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
94 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
95 return cls(False, targetName, errMsg, details)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
96
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
97 def log(self, isCompact = True) -> str:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
98 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
99 Dumps all the available information in a :str, ready for logging.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
100
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
101 Args:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
102 isCompact : if True limits the amount of information displayed to the targetName.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
103
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
104 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
105 str : information about this test result.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
106
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
107 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
108 if isCompact:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
109 return f"{TestResult.__name__}::{'Ok' if self.isPass else 'Err'}(Unit test on {self.targetName})"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
110
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
111 logMsg = f"Unit test on {self.targetName} {'passed' if self.isPass else f'failed because {self.errMsg}'}"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
112 if self.details: logMsg += f", {self.details}"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
113 return logMsg
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
114
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
115 def throw(self) -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
116 #TODO: finer Exception typing would be desirable
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
117 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
118 Logs the result information and panics.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
119
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
120 Raises:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
121 Exception : an error containing log information about the test result.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
122
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
123 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
124 None
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
125
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
126 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
127 raise Exception(self.log())
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
128
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
129 class CheckingMode:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
130 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
131 (Private) Represents a way to check a value for correctness, in the context of "testing" it.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
132 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
133
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
134 def __init__(self) -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
135 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
136 (Private) Implemented on child classes, initializes an instance of CheckingMode.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
137
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
138 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
139 None : practically, a CheckingMode instance.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
140 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
141 self.logMsg = "CheckingMode base class should not be used directly"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
142
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
143 def __checkPasses__(self, _) -> bool:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
144 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
145 (Private) Implemented on child classes, performs the actual correctness check on a received value.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
146
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
147 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
148 bool : True if the check passed, False if it failed.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
149 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
150 return True
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
151
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
152 def check(self, value) -> TestResult:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
153 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
154 Converts the :bool evaluation of the value's correctness to a TestResult.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
155
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
156 Args:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
157 value : the value to check.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
158
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
159 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
160 TestResult : the result of the check.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
161 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
162 return TestResult.Ok() if self.__checkPasses__(value) else TestResult.Err(self.logMsg, f"got {value} instead")
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
163
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
164 def __repr__(self) -> str:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
165 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
166 (Private) Implemented on child classes, formats :object as :str.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
167 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
168 return self.__class__.__name__
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
169
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
170 class ExactValue(CheckingMode):
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
171 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
172 CheckingMode subclass variant to be used when the checked value needs to match another exactly.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
173 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
174
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
175 #I suggest solving the more complex equality checking edge cases with the "Satisfies" and "MatchingShape" variants.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
176 def __init__(self, value) -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
177 self.value = value
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
178 self.logMsg = f"value needed to match {value} exactly"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
179
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
180 def __checkPasses__(self, value) -> bool:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
181 return self.value == value
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
182
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
183 def __repr__(self) -> str:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
184 return f"{super().__repr__()}({self.value})"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
185
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
186 class AcceptedValues(CheckingMode):
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
187 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
188 CheckingMode subclass variant to be used when the checked value needs to appear in a list of accepted values.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
189 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
190 def __init__(self, *values) -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
191 self.values = values
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
192 self.logMsg = f"value needed to be one of these: {values}"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
193
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
194 def __checkPasses__(self, value) -> bool:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
195 return value in self.values
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
196
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
197 def __repr__(self) -> str:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
198 return f"{super().__repr__()}{self.values}"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
199
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
200 class SatisfiesPredicate(CheckingMode):
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
201 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
202 CheckingMode subclass variant to be used when the checked value needs to verify a given predicate, as in
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
203 the predicate accepts it as input and returns True.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
204 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
205 def __init__(self, pred :Callable[..., bool], predName = "") -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
206 self.pred = pred
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
207 self.logMsg = f"value needed to verify a predicate{bool(predName) * f' called {predName}'}"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
208
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
209 def __checkPasses__(self, *params) -> bool:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
210 return self.pred(*params)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
211
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
212 def __repr__(self) -> str:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
213 return f"{super().__repr__()}(T) -> bool"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
214
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
215 class IsOfType(CheckingMode):
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
216 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
217 CheckingMode subclass variant to be used when the checked value needs to be of a certain type.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
218 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
219 def __init__(self, type :Type) -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
220 self.type = type
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
221 self.logMsg = f"value needed to be of type {type.__name__}"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
222
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
223 def __checkPasses__(self, value :Type) -> bool:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
224 return isinstance(value, self.type)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
225
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
226 def __repr__(self) -> str:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
227 return f"{super().__repr__()}:{self.type.__name__}"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
228
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
229 class Exists(CheckingMode):
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
230 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
231 CheckingMode subclass variant to be used when the checked value needs to exist (or not!). Mainly employed as a quick default
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
232 check that always passes, it still upholds its contract when it comes to checking for existing properties in objects
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
233 without much concern on what value they contain.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
234 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
235 def __init__(self, exists = True) -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
236 self.exists = exists
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
237 self.logMsg = f"value needed to {(not exists) * 'not '}exist"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
238
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
239 def __checkPasses__(self, _) -> bool: return self.exists
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
240
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
241 def __repr__(self) -> str:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
242 return f"{super().__repr__() if self.exists else 'IsMissing'}"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
243
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
244 class MatchingShape(CheckingMode):
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
245 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
246 CheckingMode subclass variant to be used when the checked value is an object that needs to have a certain shape,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
247 as in to posess properties with a given name and value. Each property is checked for existance and correctness with
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
248 its own given CheckingMode.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
249 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
250 def __init__(self, props :Dict[str, CheckingMode], objName = "") -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
251 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
252 (Private) Initializes an instance of MatchingShape.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
253
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
254 Args:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
255 props : :dict using property names as keys and checking modes for the property's value as values.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
256 objName : label for the object we're testing the shape of.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
257
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
258 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
259 None : practically, a MatchingShape instance.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
260 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
261 self.props = props
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
262 self.objName = objName
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
263
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
264 self.shapeRepr = " {\n" + "\n".join([f" {propName} : {prop}" for propName, prop in props.items()]) + "\n}"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
265
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
266 def check(self, obj :object) -> TestResult:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
267 objIsDict = isinstance(obj, dict) # Python forces us to distinguish between object properties and dict keys
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
268 for propName, checkingMode in self.props.items():
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
269 # Checking if the property exists:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
270 if (not objIsDict and not hasattr(obj, propName)) or (objIsDict and propName not in obj):
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
271 if not isinstance(checkingMode, Exists): return TestResult.Err(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
272 f"property \"{propName}\" doesn't exist on object {self.objName}", "", self.objName)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
273
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
274 if not checkingMode.exists: return TestResult.Ok(self.objName)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
275 # Either the property value is meant to be checked (checkingMode is anything but Exists)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
276 # or we want the property to not exist, all other cases are handled correctly ahead
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
277
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
278 checkRes = checkingMode.check(obj[propName] if objIsDict else getattr(obj, propName))
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
279 if checkRes.isPass: continue
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
280
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
281 checkRes.targetName = self.objName
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
282 return TestResult.Err(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
283 f"property \"{propName}\" failed check {checkingMode} on shape {obj}",
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
284 checkRes.log(isCompact = False),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
285 self.objName)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
286
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
287 return TestResult.Ok(self.objName)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
288
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
289 def __repr__(self) -> str:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
290 return super().__repr__() + self.shapeRepr
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
291
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
292 class Many(CheckingMode):
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
293 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
294 CheckingMode subclass variant to be used when the checked value is an Iterable we want to check item by item.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
295 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
296 def __init__(self, *values :CheckingMode) -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
297 self.values = values
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
298 self.shapeRepr = " [\n" + "\n".join([f" {value}" for value in values]) + "\n]"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
299
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
300 def check(self, coll :Iterable) -> TestResult:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
301 amt = len(coll)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
302 expectedAmt = len(self.values)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
303 # Length equality is forced:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
304 if amt != expectedAmt: return TestResult.Err(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
305 "items' quantities don't match", f"expected {expectedAmt} items, but got {amt}")
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
306
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
307 # Items in the given collection value are paired in order with the corresponding checkingMode meant for each of them
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
308 for item, checkingMode in zip(coll, self.values):
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
309 checkRes = checkingMode.check(item)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
310 if checkRes.isFail: return TestResult.Err(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
311 f"item in list failed check {checkingMode}",
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
312 checkRes.log(isCompact = False))
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
313
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
314 return TestResult.Ok()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
315
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
316 def __repr__(self) -> str:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
317 return super().__repr__() + self.shapeRepr
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
318
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
319 class LogMode(Enum):
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
320 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
321 Represents the level of detail of a logged message. Models 4 variants, in order of increasing detail:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
322 - Minimal : Logs the overall test result for the entire module.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
323 - Default : Also logs all single test fails, in compact mode.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
324 - Detailed : Logs all function test results, in compact mode.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
325 - Pedantic : Also logs all single test results in detailed mode.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
326 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
327 Minimal = auto()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
328 Default = auto()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
329 Detailed = auto()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
330 Pedantic = auto()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
331
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
332 def isMoreVerbose(self, requiredMode :"LogMode") -> bool:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
333 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
334 Compares the instance's level of detail with that of another.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
335
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
336 Args:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
337 requiredMode : the other instance.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
338
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
339 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
340 bool : True if the caller instance is a more detailed variant than the other.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
341 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
342 return self.value >= requiredMode.value
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
343
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
344 ## Specific Unit Testing utilities:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
345 class UnitTest:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
346 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
347 Represents a unit test, the test of a single function's isolated correctness.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
348 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
349 def __init__(self, func :Callable, inputParams :list, expectedRes :CheckingMode) -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
350 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
351 (Private) Initializes an instance of UnitTest.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
352
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
353 Args:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
354 func : the function to test.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
355 inputParams : list of parameters to pass as inputs to the function, in order.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
356 expectedRes : checkingMode to test the function's return value for correctness.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
357
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
358 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
359 None : practically, a UnitTest instance.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
360 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
361 self.func = func
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
362 self.inputParams = inputParams
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
363 self.expectedRes = expectedRes
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
364
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
365 self.funcName = func.__name__
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
366
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
367 def test(self) -> TestResult:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
368 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
369 Tests the function.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
370
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
371 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
372 TestResult : the test's result.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
373 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
374 result = None
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
375 try: result = self.func(*self.inputParams)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
376 except Exception as e: return TestResult.Err("the function panicked at runtime", e, self.funcName)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
377
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
378 checkRes = self.expectedRes.check(result)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
379 checkRes.targetName = self.funcName
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
380 return checkRes
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
381
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
382 class UnitTester:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
383 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
384 Manager class for unit testing an entire module, groups single UnitTests together and executes them in order on a
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
385 per-function basis (tests about the same function are executed consecutively) giving back as much information as
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
386 possible depending on the selected logMode. More customization options are available.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
387 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
388 def __init__(self, moduleName :str, logMode = LogMode.Default, stopOnFail = True, *funcTests :'UnitTest') -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
389 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
390 (Private) initializes an instance of UnitTester.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
391
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
392 Args:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
393 moduleName : name of the tested module.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
394 logMode : level of detail applied to all messages logged during the test.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
395 stopOnFail : if True, the test stops entirely after one unit test fails.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
396 funcTests : the unit tests to perform on the module.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
397
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
398 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
399 None : practically, a UnitTester instance.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
400 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
401 self.logMode = logMode
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
402 self.moduleName = moduleName
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
403 self.stopOnFail = stopOnFail
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
404
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
405 # This ensures the per-function order:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
406 self.funcTests :Dict[str, List[UnitTest]]= {}
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
407 for test in funcTests:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
408 if test.funcName in self.funcTests: self.funcTests[test.funcName].append(test)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
409 else: self.funcTests[test.funcName] = [test]
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
410
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
411 def logTestResult(self, testRes :TestResult) -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
412 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
413 Prints the formatted result information of a unit test.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
414
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
415 Args:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
416 testRes : the result of the test.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
417
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
418 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
419 None
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
420 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
421 if testRes.isPass: return self.log("Passed!", LogMode.Detailed, indent = 2)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
422
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
423 failMsg = "Failed! "
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
424 # Doing it this way prevents .log computations when not needed
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
425 if self.logMode.isMoreVerbose(LogMode.Detailed):
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
426 # Given that Pedantic is the most verbose variant, there's no point in comparing with LogMode.isMoreVerbose
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
427 failMsg += testRes.log(self.logMode is not LogMode.Pedantic)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
428
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
429 self.log(failMsg, indent = 2)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
430
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
431 def log(self, msg :str, minRequiredMode = LogMode.Default, indent = 0) -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
432 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
433 Prints and formats a message only when the UnitTester instance is set to a level of detail at least equal
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
434 to a minimum requirement, given as input.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
435
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
436 Args:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
437 msg : the message to print.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
438 minRequiredMode : minimum detail requirement.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
439 indent : formatting information, counter from 0 that adds 2 spaces each number up
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
440
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
441 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
442 None
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
443 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
444 if self.logMode.isMoreVerbose(minRequiredMode): print(" " * indent + msg)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
445
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
446 def testFunction(self, name :str) -> TestResult:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
447 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
448 Perform all unit tests relative to the same function, plus the surrounding logs and checks.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
449
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
450 Args:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
451 name : the name of the tested function.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
452
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
453 Returns :
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
454 TestResult : the overall Ok result of all the tests passing or the first Err. This behaviour is unrelated
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
455 to that of the overall testing procedure (stopOnFail), it always works like this for tests about the
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
456 same function.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
457 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
458 self.log(f"Unit testing {name}...", indent = 1)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
459
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
460 allPassed = True
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
461 for unitTest in self.funcTests[name]:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
462 testRes = unitTest.test()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
463 self.logTestResult(testRes)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
464 if testRes.isPass: continue
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
465
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
466 allPassed = False
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
467 if self.stopOnFail: break
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
468
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
469 self.log("", LogMode.Detailed) # Provides one extra newline of space when needed, to better format the output
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
470 if allPassed: return TestResult.Ok(name)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
471
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
472 if self.logMode is LogMode.Default: self.log("")
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
473 return TestResult.Err(f"Unlogged err", "unit test failed", name)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
474
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
475 def testModule(self) -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
476 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
477 Runs all the provided unit tests in order but on a per-function basis.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
478
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
479 Returns:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
480 None
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
481 """
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
482 self.log(f"Unit testing module {self.moduleName}...", LogMode.Minimal)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
483
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
484 fails = 0
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
485 testStatusMsg = "complete"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
486 for funcName in self.funcTests.keys():
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
487 if self.testFunction(funcName).isPass: continue
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
488 fails += 1
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
489
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
490 if self.stopOnFail:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
491 testStatusMsg = "interrupted"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
492 break
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
493
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
494 self.log(f"Testing {testStatusMsg}: {fails} problem{'s' * (fails != 1)} found.\n", LogMode.Minimal)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
495 # ^^^ Manually applied an extra newline of space.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
496
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
497 ## Unit testing all the modules:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
498 def unit_cobraxy() -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
499 import cobraxy as m
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
500 import math
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
501 import lxml.etree as ET
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
502 import utils.general_utils as utils
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
503
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
504 #m.ARGS = m.process_args()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
505
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
506 ids = ["react1", "react2", "react3", "react4", "react5"]
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
507 metabMap = utils.Model.ENGRO2.getMap()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
508 class_pat = {
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
509 "dataset1" :[
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
510 [2.3, 4, 7, 0, 0.01, math.nan, math.nan],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
511 [math.nan, math.nan, math.nan, math.nan, math.nan, math.nan, math.nan],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
512 [2.3, 4, 7, 0, 0.01, 5, 9],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
513 [math.nan, math.nan, 2.3, 4, 7, 0, 0.01],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
514 [2.3, 4, 7, math.nan, 2.3, 0, 0.01]],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
515
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
516 "dataset2" :[
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
517 [2.3, 4, 7, math.nan, 2.3, 0, 0.01],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
518 [2.3, 4, 7, 0, 0.01, math.nan, math.nan],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
519 [math.nan, math.nan, 2.3, 4, 7, 0, 0.01],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
520 [2.3, 4, 7, 0, 0.01, 5, 9],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
521 [math.nan, math.nan, math.nan, math.nan, math.nan, math.nan, math.nan]]
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
522 }
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
523
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
524 unitTester = UnitTester("cobraxy", LogMode.Pedantic, False,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
525 UnitTest(m.name_dataset, ["customName", 12], ExactValue("customName")),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
526 UnitTest(m.name_dataset, ["Dataset", 12], ExactValue("Dataset_12")),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
527
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
528 UnitTest(m.fold_change, [0.5, 0.5], ExactValue(0.0)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
529 UnitTest(m.fold_change, [0, 0.35], ExactValue("-INF")),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
530 UnitTest(m.fold_change, [0.5, 0], ExactValue("INF")),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
531 UnitTest(m.fold_change, [0, 0], ExactValue(0)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
532
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
533 UnitTest(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
534 m.Arrow(m.Arrow.MAX_W, m.ArrowColor.DownRegulated, isDashed = True).toStyleStr, [],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
535 ExactValue(";stroke:#0000FF;stroke-width:12;stroke-dasharray:5,5")),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
536
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
537 UnitTest(m.computeEnrichment, [metabMap, class_pat, ids], ExactValue(None)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
538
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
539 UnitTest(m.computePValue, [class_pat["dataset1"][0], class_pat["dataset2"][0]], SatisfiesPredicate(math.isnan)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
540
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
541 UnitTest(m.reactionIdIsDirectional, ["reactId"], ExactValue(m.ReactionDirection.Unknown)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
542 UnitTest(m.reactionIdIsDirectional, ["reactId_F"], ExactValue(m.ReactionDirection.Direct)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
543 UnitTest(m.reactionIdIsDirectional, ["reactId_B"], ExactValue(m.ReactionDirection.Inverse)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
544
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
545 UnitTest(m.ArrowColor.fromFoldChangeSign, [-2], ExactValue(m.ArrowColor.DownRegulated)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
546 UnitTest(m.ArrowColor.fromFoldChangeSign, [2], ExactValue(m.ArrowColor.UpRegulated)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
547
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
548 UnitTest(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
549 m.Arrow(m.Arrow.MAX_W, m.ArrowColor.UpRegulated).styleReactionElements,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
550 [metabMap, "reactId"],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
551 ExactValue(None)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
552
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
553 UnitTest(m.getArrowBodyElementId, ["reactId"], ExactValue("R_reactId")),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
554 UnitTest(m.getArrowBodyElementId, ["reactId_F"], ExactValue("R_reactId")),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
555
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
556 UnitTest(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
557 m.getArrowHeadElementId, ["reactId"],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
558 Many(ExactValue("F_reactId"), ExactValue("B_reactId"))),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
559
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
560 UnitTest(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
561 m.getArrowHeadElementId, ["reactId_F"],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
562 Many(ExactValue("F_reactId"), ExactValue(""))),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
563
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
564 UnitTest(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
565 m.getArrowHeadElementId, ["reactId_B"],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
566 Many(ExactValue("B_reactId"), ExactValue(""))),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
567
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
568 UnitTest(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
569 m.getElementById, ["reactId_F", metabMap],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
570 SatisfiesPredicate(lambda res : res.isErr and isinstance(res.value, utils.Result.ResultErr))),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
571
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
572 UnitTest(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
573 m.getElementById, ["F_tyr_L_t", metabMap],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
574 SatisfiesPredicate(lambda res : res.isOk and res.unwrap().get("id") == "F_tyr_L_t")),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
575 ).testModule()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
576
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
577 def unit_rps_generator() -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
578 import rps_generator as rps
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
579 import math
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
580 import pandas as pd
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
581 import utils.general_utils as utils
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
582 dataset = pd.DataFrame({
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
583 "cell lines" : ["normal", "cancer"],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
584 "pyru_vate" : [5.3, 7.01],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
585 "glu,cose" : [8.2, 4.0],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
586 "unknown" : [3.0, 3.97],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
587 "()atp" : [7.05, 8.83],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
588 })
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
589
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
590 abundancesNormalRaw = {
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
591 "pyru_vate" : 5.3,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
592 "glu,cose" : 8.2,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
593 "unknown" : 3.0,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
594 "()atp" : 7.05,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
595 }
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
596
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
597 abundancesNormal = {
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
598 "pyr" : 5.3,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
599 "glc__D" : 8.2,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
600 "atp" : 7.05,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
601 }
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
602
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
603 # TODO: this currently doesn't work due to "the pickle extension problem", see FileFormat class for details.
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
604 synsDict = utils.readPickle(utils.FilePath("synonyms", utils.FileFormat.PICKLE, prefix = "./local/pickle files"))
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
605
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
606 reactionsDict = {
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
607 "r1" : {
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
608 "glc__D" : 1
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
609 },
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
610
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
611 "r2" : {
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
612 "co2" : 2,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
613 "pyr" : 3,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
614 },
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
615
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
616 "r3" : {
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
617 "atp" : 2,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
618 "glc__D" : 4,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
619 },
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
620
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
621 "r4" : {
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
622 "atp" : 3,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
623 }
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
624 }
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
625
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
626 abundancesNormalEdited = {
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
627 "pyr" : 5.3,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
628 "glc__D" : 8.2,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
629 "atp" : 7.05,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
630 "co2" : 1,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
631 }
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
632
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
633 blackList = ["atp"] # No jokes allowed!
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
634 missingInDataset = ["co2"]
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
635
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
636 normalRpsShape = MatchingShape({
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
637 "r1" : ExactValue(8.2 ** 1),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
638 "r2" : ExactValue((1 ** 2) * (5.3 ** 3)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
639 "r3" : ExactValue((8.2 ** 4) * (7.05 ** 2)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
640 "r4" : SatisfiesPredicate(lambda n : math.isnan(n))
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
641 }, "rps dict")
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
642
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
643 UnitTester("rps_generator", LogMode.Pedantic, False,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
644 UnitTest(rps.get_abund_data, [dataset, 0], MatchingShape({
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
645 "pyru_vate" : ExactValue(5.3),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
646 "glu,cose" : ExactValue(8.2),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
647 "unknown" : ExactValue(3.0),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
648 "()atp" : ExactValue(7.05),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
649 "name" : ExactValue("normal")
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
650 }, "abundance series")),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
651
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
652 UnitTest(rps.get_abund_data, [dataset, 1], MatchingShape({
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
653 "pyru_vate" : ExactValue(7.01),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
654 "glu,cose" : ExactValue(4.0),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
655 "unknown" : ExactValue(3.97),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
656 "()atp" : ExactValue(8.83),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
657 "name" : ExactValue("cancer")
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
658 }, "abundance series")),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
659
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
660 UnitTest(rps.get_abund_data, [dataset, -1], ExactValue(None)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
661
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
662 UnitTest(rps.check_missing_metab, [reactionsDict, abundancesNormal.copy()], Many(MatchingShape({
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
663 "pyr" : ExactValue(5.3),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
664 "glc__D" : ExactValue(8.2),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
665 "atp" : ExactValue(7.05),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
666 "co2" : ExactValue(1)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
667 }, "updated abundances"), Many(ExactValue("co2")))),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
668
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
669 UnitTest(rps.clean_metabolite_name, ["4,4'-diphenylmethane diisocyanate"], ExactValue("44diphenylmethanediisocyanate")),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
670
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
671 UnitTest(rps.get_metabolite_id, ["tryptophan", synsDict], ExactValue("trp__L")),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
672
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
673 UnitTest(rps.calculate_rps, [reactionsDict, abundancesNormalEdited, blackList, missingInDataset], normalRpsShape),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
674
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
675 UnitTest(rps.rps_for_cell_lines, [dataset, reactionsDict, blackList, synsDict, "", True], Many(normalRpsShape, MatchingShape({
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
676 "r1" : ExactValue(4.0 ** 1),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
677 "r2" : ExactValue((1 ** 2) * (7.01 ** 3)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
678 "r3" : ExactValue((4.0 ** 4) * (8.83 ** 2)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
679 "r4" : SatisfiesPredicate(lambda n : math.isnan(n))
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
680 }, "rps dict"))),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
681
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
682 #UnitTest(rps.main, [], ExactValue(None)) # Complains about sys argvs
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
683 ).testModule()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
684
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
685 def unit_custom_data_generator() -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
686 import custom_data_generator as cdg
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
687
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
688 UnitTester("custom data generator", LogMode.Pedantic, False,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
689 UnitTest(lambda :True, [], ExactValue(True)), # No tests can be done without a model at hand!
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
690 ).testModule()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
691
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
692 def unit_utils() -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
693 import utils.general_utils as utils
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
694 import utils.rule_parsing as ruleUtils
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
695 import utils.reaction_parsing as reactionUtils
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
696
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
697 UnitTester("utils", LogMode.Pedantic, False,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
698 UnitTest(utils.CustomErr, ["myMsg", "more details"], MatchingShape({
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
699 "details" : ExactValue("more details"),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
700 "msg" : ExactValue("myMsg"),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
701 "id" : ExactValue(0) # this will fail if any custom errors happen anywhere else before!
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
702 })),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
703
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
704 UnitTest(utils.CustomErr, ["myMsg", "more details", 42], MatchingShape({
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
705 "details" : ExactValue("more details"),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
706 "msg" : ExactValue("myMsg"),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
707 "id" : ExactValue(42)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
708 })),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
709
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
710 UnitTest(utils.Bool("someArg").check, ["TrUe"], ExactValue(True)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
711 UnitTest(utils.Bool("someArg").check, ["FALse"], ExactValue(False)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
712 UnitTest(utils.Bool("someArg").check, ["foo"], Exists(False)), # should panic!
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
713
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
714 UnitTest(utils.Model.ENGRO2.getRules, ["."], IsOfType(dict)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
715 UnitTest(utils.Model.Custom.getRules, [".", ""], Exists(False)), # expected panic
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
716
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
717 # rule utilities tests:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
718 UnitTest(ruleUtils.parseRuleToNestedList, ["A"], Many(ExactValue("A"))),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
719 UnitTest(ruleUtils.parseRuleToNestedList, ["A or B"], Many(ExactValue("A"), ExactValue("B"))),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
720 UnitTest(ruleUtils.parseRuleToNestedList, ["A and B"], Many(ExactValue("A"), ExactValue("B"))),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
721 UnitTest(ruleUtils.parseRuleToNestedList, ["A foo B"], Exists(False)), # expected panic
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
722 UnitTest(ruleUtils.parseRuleToNestedList, ["A)"], Exists(False)), # expected panic
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
723
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
724 UnitTest(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
725 ruleUtils.parseRuleToNestedList, ["A or B"],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
726 MatchingShape({ "op" : ExactValue(ruleUtils.RuleOp.OR)})),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
727
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
728 UnitTest(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
729 ruleUtils.parseRuleToNestedList, ["A and B"],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
730 MatchingShape({ "op" : ExactValue(ruleUtils.RuleOp.AND)})),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
731
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
732 UnitTest(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
733 ruleUtils.parseRuleToNestedList, ["A or B and C"],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
734 MatchingShape({ "op" : ExactValue(ruleUtils.RuleOp.OR)})),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
735
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
736 UnitTest(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
737 ruleUtils.parseRuleToNestedList, ["A or B and C or (D and E)"],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
738 Many(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
739 ExactValue("A"),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
740 Many(ExactValue("B"), ExactValue("C")),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
741 Many(ExactValue("D"), ExactValue("E"))
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
742 )),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
743
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
744 UnitTest(lambda s : ruleUtils.RuleOp(s), ["or"], ExactValue(ruleUtils.RuleOp.OR)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
745 UnitTest(lambda s : ruleUtils.RuleOp(s), ["and"], ExactValue(ruleUtils.RuleOp.AND)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
746 UnitTest(lambda s : ruleUtils.RuleOp(s), ["foo"], Exists(False)), # expected panic
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
747
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
748 UnitTest(ruleUtils.RuleOp.isOperator, ["or"], ExactValue(True)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
749 UnitTest(ruleUtils.RuleOp.isOperator, ["and"], ExactValue(True)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
750 UnitTest(ruleUtils.RuleOp.isOperator, ["foo"], ExactValue(False)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
751
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
752 # reaction utilities tests:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
753 UnitTest(reactionUtils.ReactionDir.fromReaction, ["atp <=> adp + pi"], ExactValue(reactionUtils.ReactionDir.REVERSIBLE)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
754 UnitTest(reactionUtils.ReactionDir.fromReaction, ["atp --> adp + pi"], ExactValue(reactionUtils.ReactionDir.FORWARD)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
755 UnitTest(reactionUtils.ReactionDir.fromReaction, ["atp <-- adp + pi"], ExactValue(reactionUtils.ReactionDir.BACKWARD)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
756 UnitTest(reactionUtils.ReactionDir.fromReaction, ["atp ??? adp + pi"], Exists(False)), # should panic
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
757
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
758 UnitTest(
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
759 reactionUtils.create_reaction_dict,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
760 [{'shdgd': '2 pyruvate + 1 h2o <=> 1 h2o + 2 acetate', 'sgwrw': '2 co2 + 6 h2o --> 3 atp'}],
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
761 MatchingShape({
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
762 "shdgd_B" : MatchingShape({
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
763 "acetate" : ExactValue(2),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
764 "h2o" : ExactValue(1),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
765 }),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
766
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
767 "shdgd_F" : MatchingShape({
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
768 "pyruvate" : ExactValue(2),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
769 "h2o" : ExactValue(1)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
770 }),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
771
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
772 "sgwrw" : MatchingShape({
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
773 "co2" : ExactValue(2),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
774 "h2o" : ExactValue(6),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
775 })
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
776 }, "reaction dict")),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
777 ).testModule()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
778
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
779 rule = "A and B or C or D and (E or F and G) or H"
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
780 print(f"rule \"{rule}\" should comes out as: {ruleUtils.parseRuleToNestedList(rule)}")
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
781
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
782 def unit_ras_generator() -> None:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
783 import ras_generator as ras
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
784 import utils.rule_parsing as ruleUtils
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
785
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
786 # Making an alias to mask the name of the inner function and separate the 2 tests:
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
787 def opListAlias(op_list, dataset):
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
788 ras.ARGS.none = False
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
789 return ras.ras_op_list(op_list, dataset)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
790
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
791 ras.ARGS = ras.process_args()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
792 rule = ruleUtils.OpList(ruleUtils.RuleOp.AND)
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
793 rule.extend(["foo", "bar", "baz"])
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
794
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
795 dataset = { "foo" : 5, "bar" : 2, "baz" : None }
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
796
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
797 UnitTester("ras generator", LogMode.Pedantic, False,
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
798 UnitTest(ras.ras_op_list, [rule, dataset], ExactValue(2)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
799 UnitTest(opListAlias, [rule, dataset], ExactValue(None)),
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
800 ).testModule()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
801
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
802 if __name__ == "__main__":
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
803 unit_cobraxy()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
804 unit_custom_data_generator()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
805 unit_utils()
41f35c2f0c7b Uploaded
luca_milaz
parents:
diff changeset
806 unit_ras_generator()