|
1 | 1 | import json
|
2 | 2 | import os
|
| 3 | +import copy |
3 | 4 | from enum import Enum
|
4 | 5 |
|
5 | 6 | from regression.executable_runner import ExecutableRunner
|
@@ -191,6 +192,48 @@ def check_contains_precise_evs(self, expected_number=1, is_initializer=False, ac
|
191 | 192 | def get_dynamic_object_ids(self):
|
192 | 193 | return [dynamic_object.instance['id'] for dynamic_object in self.dynamic_objects]
|
193 | 194 |
|
| 195 | +def report_difference_with_context(state1, state2, context): |
| 196 | + if type(state1) != type(state2): |
| 197 | + return (context, str(type(state1)), str(type(state2))) |
| 198 | + if isinstance(state1, list) or \ |
| 199 | + isinstance(state1, dict) or \ |
| 200 | + isinstance(state1, set): |
| 201 | + if len(state1) != len(state2): |
| 202 | + return(context + ["length"], len(state1), len(state2)) |
| 203 | + context.append(None) |
| 204 | + if isinstance(state1, list): |
| 205 | + for (idx, (element1, element2)) in enumerate(zip(state1, state2)): |
| 206 | + context[-1] = idx |
| 207 | + subrep = \ |
| 208 | + report_difference_with_context(element1, element2, context) |
| 209 | + if subrep is not None: |
| 210 | + return subrep |
| 211 | + elif isinstance(state1, set): |
| 212 | + for element1 in state1: |
| 213 | + if element1 not in state2: |
| 214 | + return (context, element1, "Element not present in set") |
| 215 | + else: |
| 216 | + for key in state1: |
| 217 | + if key not in state2: |
| 218 | + return (context, "Key {} present".format(key), "Key absent") |
| 219 | + context[-1] = key |
| 220 | + subrep = report_difference_with_context( \ |
| 221 | + state1[key], state2[key], context) |
| 222 | + if subrep is not None: |
| 223 | + return subrep |
| 224 | + context.pop() |
| 225 | + else: |
| 226 | + if state1 != state2: |
| 227 | + return (context, state1, state2) |
| 228 | + return None |
| 229 | + |
| 230 | +def report_first_state_difference(state1, state2, desc1, desc2): |
| 231 | + state_difference = report_difference_with_context(state1, state2, []) |
| 232 | + if state_difference is not None: |
| 233 | + return "In context: {}\n{}: {}\n{}: {}".format( |
| 234 | + state_difference[0], |
| 235 | + desc1, state_difference[1], |
| 236 | + desc2, state_difference[2]) |
194 | 237 |
|
195 | 238 | class LvsaExpectation:
|
196 | 239 | """Encapsulate the output of LvsaDriver"""
|
@@ -281,6 +324,9 @@ def get_value_set_for_dynamic_object_value_set(self, dynamic_object_id, suffix,
|
281 | 324 | def get_all_value_sets(self):
|
282 | 325 | return [ValueSetExpectation(var_state) for var_state in self.state]
|
283 | 326 |
|
| 327 | + def report_first_difference(self, other, self_desc, other_desc): |
| 328 | + return report_first_state_difference( |
| 329 | + self.state, other.state, self_desc, other_desc) |
284 | 330 |
|
285 | 331 | class LvsaDriver:
|
286 | 332 | """Run LVSA"""
|
@@ -324,4 +370,52 @@ def run(self):
|
324 | 370 | executable_runner = ExecutableRunner(cmd)
|
325 | 371 | (stdout, _, _) = executable_runner.run()
|
326 | 372 |
|
327 |
| - return LvsaExpectation(stdout, fq_class_name, fq_function_name) |
| 373 | + result = LvsaExpectation(stdout, fq_class_name, fq_function_name) |
| 374 | + |
| 375 | + # Check that LVSA's selective domain storage is working correctly: also |
| 376 | + # run this command in debug mode, which retains domains for all program |
| 377 | + # points, and check it has the same results. |
| 378 | + |
| 379 | + debug_cmd = copy.copy(cmd) |
| 380 | + debug_cmd.append("--lvsa-show-all-program-points") |
| 381 | + |
| 382 | + debug_runner = ExecutableRunner(debug_cmd) |
| 383 | + (debug_stdout, _, _) = debug_runner.run() |
| 384 | + |
| 385 | + debug_result = \ |
| 386 | + LvsaExpectation(debug_stdout, fq_class_name, fq_function_name) |
| 387 | + |
| 388 | + difference = result.report_first_difference( |
| 389 | + debug_result, "Selective domain storage", "Storing all domains") |
| 390 | + |
| 391 | + if difference is not None: |
| 392 | + raise Exception(difference) |
| 393 | + |
| 394 | + # Further check that the results with domain reconstruction (when |
| 395 | + # program points are rebuilt from those selectively stored) exactly |
| 396 | + # match those produced by retaining all domains in the first place: |
| 397 | + |
| 398 | + test_reconstruction_cmd = copy.copy(debug_cmd) |
| 399 | + test_reconstruction_cmd.append( |
| 400 | + "--lvsa-reconstruct-intermediate-program-points") |
| 401 | + |
| 402 | + test_reconstruction_runner = ExecutableRunner(test_reconstruction_cmd) |
| 403 | + (test_reconstruction_stdout, _, _) = test_reconstruction_runner.run() |
| 404 | + |
| 405 | + debug_full_result = json.loads(debug_stdout) |
| 406 | + debug_full_result = \ |
| 407 | + [x for x in debug_full_result if x.get('messageType') == 'LVSA-ALL-FUNCTIONS-DUMP'] |
| 408 | + test_reconstruction_full_result = json.loads(test_reconstruction_stdout) |
| 409 | + test_reconstruction_full_result = \ |
| 410 | + [x for x in test_reconstruction_full_result if x.get('messageType') == 'LVSA-ALL-FUNCTIONS-DUMP'] |
| 411 | + |
| 412 | + reconstruction_difference = report_first_state_difference( |
| 413 | + debug_full_result, |
| 414 | + test_reconstruction_full_result, |
| 415 | + "Conventionally computed domains", |
| 416 | + "Reconstructed intermediate domains") |
| 417 | + |
| 418 | + if reconstruction_difference is not None: |
| 419 | + raise Exception(reconstruction_difference) |
| 420 | + |
| 421 | + return result |
0 commit comments