From de04bf85a087d2c914b30aaa4e3d563e53b06454 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Wed, 26 Oct 2022 10:34:34 -0700 Subject: Fixed taskmaster trace tests. Previously there was an extra line at the end of the file. It's no longer there. Added TestCommon.detailed_diff() function which can be used to diff large text blobs expected vs actual --- SCons/Taskmaster/TaskmasterTests.py | 107 ++++++++++++++++++++---------------- test/Interactive/taskmastertrace.py | 11 ++-- test/option/taskmastertrace.py | 9 +-- testing/framework/TestCommon.py | 16 ++++++ 4 files changed, 84 insertions(+), 59 deletions(-) diff --git a/SCons/Taskmaster/TaskmasterTests.py b/SCons/Taskmaster/TaskmasterTests.py index fad028e..9d7f959 100644 --- a/SCons/Taskmaster/TaskmasterTests.py +++ b/SCons/Taskmaster/TaskmasterTests.py @@ -26,10 +26,10 @@ import SCons.compat import sys import unittest - import SCons.Taskmaster import SCons.Errors +import TestCommon built_text = None cache_text = [] @@ -37,8 +37,9 @@ visited_nodes = [] executed = None scan_called = 0 + class Node: - def __init__(self, name, kids = [], scans = []): + def __init__(self, name, kids=[], scans=[]): self.name = name self.kids = kids self.scans = scans @@ -47,9 +48,11 @@ class Node: self.scanner = None self.targets = [self] self.prerequisites = None + class Builder: def targets(self, node): return node.targets + self.builder = Builder() self.bsig = None self.csig = None @@ -83,7 +86,7 @@ class Node: def prepare(self): self.prepared = 1 - self.get_binfo() + self.get_binfo() def build(self): global built_text @@ -119,7 +122,7 @@ class Node: self.binfo = binfo return binfo - + def clear(self): # The del_binfo() call here isn't necessary for normal execution, # but is for interactive mode, where we might rebuild the same @@ -130,14 +133,14 @@ class Node: global built_text if not self.cached: built_text = built_text + " really" - + # Clear the implicit dependency caches of any Nodes # waiting for this Node to be built. for parent in self.waiting_parents: parent.implicit = None self.clear() - + def release_target_info(self): pass @@ -199,7 +202,7 @@ class Node: def is_up_to_date(self): return self._current_val - + def depends_on(self, nodes): for node in nodes: if node in self.kids: @@ -218,26 +221,34 @@ class Node: class Executor: def prepare(self): pass + def get_action_targets(self): return self.targets + def get_all_targets(self): return self.targets + def get_all_children(self): result = [] for node in self.targets: result.extend(node.children()) return result + def get_all_prerequisites(self): return [] + def get_action_side_effects(self): return [] + self.executor = Executor() self.executor.targets = self.targets return self.executor + class OtherError(Exception): pass + class MyException(Exception): pass @@ -306,7 +317,7 @@ class TaskmasterTestCase(unittest.TestCase): n2._current_val = 1 n3.set_state(SCons.Node.no_state) n3._current_val = 1 - tm = SCons.Taskmaster.Taskmaster(targets = [n3], tasker = MyTask) + tm = SCons.Taskmaster.Taskmaster(targets=[n3], tasker=MyTask) t = tm.next_task() t.prepare() @@ -331,7 +342,6 @@ class TaskmasterTestCase(unittest.TestCase): assert tm.next_task() is None - n1 = Node("n1") n2 = Node("n2") n3 = Node("n3", [n1, n2]) @@ -366,7 +376,6 @@ class TaskmasterTestCase(unittest.TestCase): assert tm.next_task() is None - n4 = Node("n4") n4.set_state(SCons.Node.executed) tm = SCons.Taskmaster.Taskmaster([n4]) @@ -374,14 +383,13 @@ class TaskmasterTestCase(unittest.TestCase): n1 = Node("n1") n2 = Node("n2", [n1]) - tm = SCons.Taskmaster.Taskmaster([n2,n2]) + tm = SCons.Taskmaster.Taskmaster([n2, n2]) t = tm.next_task() t.executed() t.postprocess() t = tm.next_task() assert tm.next_task() is None - n1 = Node("n1") n2 = Node("n2") n3 = Node("n3", [n1], [n2]) @@ -440,11 +448,11 @@ class TaskmasterTestCase(unittest.TestCase): n1 = Node("n1") n2 = Node("n2") n3 = Node("n3") - n4 = Node("n4", [n1,n2,n3]) + n4 = Node("n4", [n1, n2, n3]) n5 = Node("n5", [n4]) n3.side_effect = 1 n1.side_effects = n2.side_effects = n3.side_effects = [n4] - tm = SCons.Taskmaster.Taskmaster([n1,n2,n3,n4,n5]) + tm = SCons.Taskmaster.Taskmaster([n1, n2, n3, n4, n5]) t = tm.next_task() assert t.get_target() == n1 assert n4.state == SCons.Node.executing, n4.state @@ -471,10 +479,12 @@ class TaskmasterTestCase(unittest.TestCase): n1 = Node("n1") n2 = Node("n2") n3 = Node("n3") - n4 = Node("n4", [n1,n2,n3]) + n4 = Node("n4", [n1, n2, n3]) + def reverse(dependencies): dependencies.reverse() return dependencies + tm = SCons.Taskmaster.Taskmaster([n4], order=reverse) t = tm.next_task() assert t.get_target() == n3, t.get_target() @@ -534,11 +544,11 @@ class TaskmasterTestCase(unittest.TestCase): s = n2.get_state() assert s == SCons.Node.executed, s - def test_make_ready_out_of_date(self): """Test the Task.make_ready() method's list of out-of-date Nodes """ ood = [] + def TaskGen(tm, targets, top, node, ood=ood): class MyTask(SCons.Taskmaster.AlwaysTask): def make_ready(self): @@ -558,8 +568,8 @@ class TaskmasterTestCase(unittest.TestCase): a5 = Node("a5") a5._current_val = 1 a5.always_build = 1 - tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4, a5], - tasker = TaskGen) + tm = SCons.Taskmaster.Taskmaster(targets=[n1, c2, n3, c4, a5], + tasker=TaskGen) del ood[:] t = tm.next_task() @@ -584,12 +594,13 @@ class TaskmasterTestCase(unittest.TestCase): def test_make_ready_exception(self): """Test handling exceptions from Task.make_ready() """ + class MyTask(SCons.Taskmaster.AlwaysTask): def make_ready(self): raise MyException("from make_ready()") n1 = Node("n1") - tm = SCons.Taskmaster.Taskmaster(targets = [n1], tasker = MyTask) + tm = SCons.Taskmaster.Taskmaster(targets=[n1], tasker=MyTask) t = tm.next_task() exc_type, exc_value, exc_tb = t.exception assert exc_type == MyException, repr(exc_type) @@ -601,6 +612,7 @@ class TaskmasterTestCase(unittest.TestCase): We should be getting: TypeError: Can't instantiate abstract class MyTask with abstract methods needs_execute """ + class MyTask(SCons.Taskmaster.Task): pass @@ -611,6 +623,7 @@ class TaskmasterTestCase(unittest.TestCase): def test_make_ready_all(self): """Test the make_ready_all() method""" + class MyTask(SCons.Taskmaster.AlwaysTask): make_ready = SCons.Taskmaster.Task.make_ready_all @@ -621,7 +634,7 @@ class TaskmasterTestCase(unittest.TestCase): c4 = Node("c4") c4._current_val = 1 - tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4]) + tm = SCons.Taskmaster.Taskmaster(targets=[n1, c2, n3, c4]) t = tm.next_task() target = t.get_target() @@ -647,8 +660,8 @@ class TaskmasterTestCase(unittest.TestCase): n3 = Node("n3") c4 = Node("c4") - tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4], - tasker = MyTask) + tm = SCons.Taskmaster.Taskmaster(targets=[n1, c2, n3, c4], + tasker=MyTask) t = tm.next_task() target = t.get_target() @@ -669,13 +682,14 @@ class TaskmasterTestCase(unittest.TestCase): t = tm.next_task() assert t is None - def test_children_errors(self): """Test errors when fetching the children of a node. """ + class StopNode(Node): def children(self): raise SCons.Errors.StopError("stop!") + class ExitNode(Node): def children(self): sys.exit(77) @@ -879,8 +893,8 @@ class TaskmasterTestCase(unittest.TestCase): n9 = Node("n9") n10 = Node("n10") - n6.side_effects = [ n8 ] - n7.side_effects = [ n9, n10 ] + n6.side_effects = [n8] + n7.side_effects = [n9, n10] tm = SCons.Taskmaster.Taskmaster([n6, n7]) t = tm.next_task() @@ -897,15 +911,19 @@ class TaskmasterTestCase(unittest.TestCase): class ExceptionExecutor: def prepare(self): raise Exception("Executor.prepare() exception") + def get_all_targets(self): return self.nodes + def get_all_children(self): result = [] for node in self.nodes: result.extend(node.children()) return result + def get_all_prerequisites(self): return [] + def get_action_side_effects(self): return [] @@ -935,6 +953,7 @@ class TaskmasterTestCase(unittest.TestCase): def raise_UserError(): raise SCons.Errors.UserError + n2 = Node("n2") n2.build = raise_UserError tm = SCons.Taskmaster.Taskmaster([n2]) @@ -948,6 +967,7 @@ class TaskmasterTestCase(unittest.TestCase): def raise_BuildError(): raise SCons.Errors.BuildError + n3 = Node("n3") n3.build = raise_BuildError tm = SCons.Taskmaster.Taskmaster([n3]) @@ -964,6 +984,7 @@ class TaskmasterTestCase(unittest.TestCase): # args set to the exception value, instance, and traceback. def raise_OtherError(): raise OtherError + n4 = Node("n4") n4.build = raise_OtherError tm = SCons.Taskmaster.Taskmaster([n4]) @@ -1066,7 +1087,7 @@ class TaskmasterTestCase(unittest.TestCase): """ n1 = Node("n1") tm = SCons.Taskmaster.Taskmaster([n1]) - t = tm.next_task() + t = tm.next_task() t.exception_set((1, 2)) exc_type, exc_value = t.exception @@ -1076,25 +1097,26 @@ class TaskmasterTestCase(unittest.TestCase): t.exception_set(3) assert t.exception == 3 - try: 1//0 + try: + 1 // 0 except: # Moved from below t.exception_set(None) - #pass + # pass -# import pdb; pdb.set_trace() + # import pdb; pdb.set_trace() # Having this here works for python 2.x, # but it is a tuple (None, None, None) when called outside # an except statement # t.exception_set(None) - + exc_type, exc_value, exc_tb = t.exception - assert exc_type is ZeroDivisionError, "Expecting ZeroDevisionError got:%s"%exc_type + assert exc_type is ZeroDivisionError, "Expecting ZeroDevisionError got:%s" % exc_type exception_values = [ "integer division or modulo", "integer division or modulo by zero", - "integer division by zero", # PyPy2 + "integer division by zero", # PyPy2 ] assert str(exc_value) in exception_values, exc_value @@ -1108,7 +1130,7 @@ class TaskmasterTestCase(unittest.TestCase): except: exc_type, exc_value = sys.exc_info()[:2] assert exc_type == Exception1, exc_type - assert str(exc_value) == '', "Expecting empty string got:%s (type %s)"%(exc_value,type(exc_value)) + assert str(exc_value) == '', "Expecting empty string got:%s (type %s)" % (exc_value, type(exc_value)) else: assert 0, "did not catch expected exception" @@ -1129,7 +1151,7 @@ class TaskmasterTestCase(unittest.TestCase): pass try: - 1//0 + 1 // 0 except: tb = sys.exc_info()[2] t.exception_set((Exception3, "arg", tb)) @@ -1242,18 +1264,11 @@ Task.postprocess(): node Taskmaster: Looking for a node to evaluate Taskmaster: No candidate anymore. """ - v_split=value.split('\n') - e_split=expect.split('\n') - if len(v_split) != len(e_split): - print("different number of lines:%d %d" % (len(v_split), len(e_split))) - - # breakpoint() - for v, e in zip(v_split, e_split): - # print("%s:%s"%(v,e)) - if v != e: - print("\n[%s]\n[%s]" % (v, e)) - - assert value == expect, "Expected:\n%s\nGot:\n%s" % (expect, value) + + if value != expect: + TestCommon.TestCommon.detailed_diff(value, expect) + + assert value == expect, "Expected taskmaster trace contents didn't match. See above" if __name__ == "__main__": diff --git a/test/Interactive/taskmastertrace.py b/test/Interactive/taskmastertrace.py index 93ee068..04e95fd 100644 --- a/test/Interactive/taskmastertrace.py +++ b/test/Interactive/taskmastertrace.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,8 +22,7 @@ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + """ Verify use of the --taskmastertrace= option to the "build" command @@ -42,7 +43,6 @@ Command('2', [], Touch('$TARGET')) test.write('foo.in', "foo.in 1\n") - scons = test.start(arguments = '-Q --interactive') scons.send("build foo.out 1\n") @@ -101,7 +101,6 @@ Task.postprocess(): node Taskmaster: Looking for a node to evaluate Taskmaster: No candidate anymore. - scons>>> Touch("2") scons>>> scons: `foo.out' is up to date. scons>>> @@ -109,8 +108,6 @@ scons>>> test.finish(scons, stdout = expect_stdout) - - test.pass_test() # Local Variables: diff --git a/test/option/taskmastertrace.py b/test/option/taskmastertrace.py index fc8522c..9d0a606 100644 --- a/test/option/taskmastertrace.py +++ b/test/option/taskmastertrace.py @@ -1,6 +1,8 @@ #!/usr/bin/env python # -# __COPYRIGHT__ +# MIT License +# +# Copyright The SCons Foundation # # Permission is hereby granted, free of charge, to any person obtaining # a copy of this software and associated documentation files (the @@ -20,9 +22,6 @@ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" """ Simple tests of the --taskmastertrace= option. @@ -123,7 +122,6 @@ Task.postprocess(): node Taskmaster: Looking for a node to evaluate Taskmaster: No candidate anymore. - """) test.run(arguments='--taskmastertrace=- .', stdout=expect_stdout) @@ -212,7 +210,6 @@ Task.postprocess(): node Taskmaster: Looking for a node to evaluate Taskmaster: No candidate anymore. - """ test.must_match('trace.out', expect_trace, mode='r') diff --git a/testing/framework/TestCommon.py b/testing/framework/TestCommon.py index f45b856..df005b3 100644 --- a/testing/framework/TestCommon.py +++ b/testing/framework/TestCommon.py @@ -796,6 +796,22 @@ class TestCommon(TestCmd): # so this is an Aegis invocation; pass the test (exit 0). self.pass_test() + @staticmethod + def detailed_diff(value, expect): + v_split = value.split('\n') + e_split = expect.split('\n') + if len(v_split) != len(e_split): + print("different number of lines:%d %d" % (len(v_split), len(e_split))) + + # breakpoint() + for v, e in zip(v_split, e_split): + # print("%s:%s"%(v,e)) + if v != e: + print("\n[%s]\n[%s]" % (v, e)) + + return "Expected:\n%s\nGot:\n%s" % (expect, value) + + # Local Variables: # tab-width:4 # indent-tabs-mode:nil -- cgit v0.12