Merged revisions 1884-1905 via svnmerge from
[scons.git] / src / engine / SCons / TaskmasterTests.py
1 #
2 # __COPYRIGHT__
3 #
4 # Permission is hereby granted, free of charge, to any person obtaining
5 # a copy of this software and associated documentation files (the
6 # "Software"), to deal in the Software without restriction, including
7 # without limitation the rights to use, copy, modify, merge, publish,
8 # distribute, sublicense, and/or sell copies of the Software, and to
9 # permit persons to whom the Software is furnished to do so, subject to
10 # the following conditions:
11 #
12 # The above copyright notice and this permission notice shall be included
13 # in all copies or substantial portions of the Software.
14 #
15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
16 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
17 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22 #
23
24 __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
25
26 import copy
27 import sys
28 import unittest
29
30 import SCons.Taskmaster
31 import SCons.Errors
32
33
34 built_text = None
35 cache_text = []
36 visited_nodes = []
37 executed = None
38 scan_called = 0
39
40 class Node:
41     def __init__(self, name, kids = [], scans = []):
42         self.name = name
43         self.kids = kids
44         self.scans = scans
45         self.cached = 0
46         self.scanned = 0
47         self.scanner = None
48         self.targets = [self]
49         class Builder:
50             def targets(self, node):
51                 return node.targets
52         self.builder = Builder()
53         self.bsig = None
54         self.csig = None
55         self.state = SCons.Node.no_state
56         self.prepared = None
57         self.ref_count = 0
58         self.waiting_parents = {}
59         self.waiting_s_e = {}
60         self.side_effect = 0
61         self.side_effects = []
62         self.alttargets = []
63         self.postprocessed = None
64         self._bsig_val = None
65         self._current_val = 0
66
67     def disambiguate(self):
68         return self
69
70     def retrieve_from_cache(self):
71         global cache_text
72         if self.cached:
73             cache_text.append(self.name + " retrieved")
74         return self.cached
75
76     def build(self):
77         global built_text
78         built_text = self.name + " built"
79
80     def has_builder(self):
81         return not self.builder is None
82
83     def is_derived(self):
84         return self.has_builder or self.side_effect
85
86     def alter_targets(self):
87         return self.alttargets, None
88
89     def built(self):
90         global built_text
91         built_text = built_text + " really"
92
93     def visited(self):
94         global visited_nodes
95         visited_nodes.append(self.name)
96
97     def prepare(self):
98         self.prepared = 1
99
100     def children(self):
101         if not self.scanned:
102             self.scan()
103             self.scanned = 1
104         return self.kids
105
106     def scan(self):
107         global scan_called
108         scan_called = scan_called + 1
109         self.kids = self.kids + self.scans
110         self.scans = []
111
112     def scanner_key(self):
113         return self.name
114
115     def add_to_waiting_parents(self, node):
116         wp = self.waiting_parents
117         if wp.has_key(node):
118             result = 0
119         else:
120             result = 1
121         wp[node] = 1
122         return result
123
124     def call_for_all_waiting_parents(self, func):
125         func(self)
126         for parent in self.waiting_parents.keys():
127             parent.call_for_all_waiting_parents(func)
128
129     def get_state(self):
130         return self.state
131
132     def set_state(self, state):
133         self.state = state
134
135     def set_bsig(self, bsig):
136         self.bsig = bsig
137
138     def set_csig(self, csig):
139         self.csig = csig
140
141     def store_csig(self):
142         pass
143
144     def store_bsig(self):
145         pass
146
147     def calculator(self):
148         class Calc:
149             def bsig(self, node):
150                 return node._bsig_val
151             def current(self, node, sig):
152                 return node._current_val
153         return Calc()
154
155     def current(self, calc=None):
156         if calc is None:
157             calc = self.calculator()
158         return calc.current(self, calc.bsig(self))
159     
160     def depends_on(self, nodes):
161         for node in nodes:
162             if node in self.kids:
163                 return 1
164         return 0
165
166     def __str__(self):
167         return self.name
168
169     def postprocess(self):
170         self.postprocessed = 1
171
172     def get_executor(self):
173         class Executor:
174             pass
175         e = Executor()
176         e.targets = self.targets
177         return e
178
179 class OtherError(Exception):
180     pass
181
182 class MyException(Exception):
183     pass
184
185
186 class TaskmasterTestCase(unittest.TestCase):
187
188     def test_next_task(self):
189         """Test fetching the next task
190         """
191         global built_text
192
193         n1 = Node("n1")
194         tm = SCons.Taskmaster.Taskmaster([n1, n1])
195         t = tm.next_task()
196         t.prepare()
197         t.execute()
198         t = tm.next_task()
199         assert t == None
200
201         n1 = Node("n1")
202         n2 = Node("n2")
203         n3 = Node("n3", [n1, n2])
204
205         tm = SCons.Taskmaster.Taskmaster([n3])
206
207         t = tm.next_task()
208         t.prepare()
209         t.execute()
210         assert built_text == "n1 built", built_text
211         t.executed()
212         t.postprocess()
213
214         t = tm.next_task()
215         t.prepare()
216         t.execute()
217         assert built_text == "n2 built", built_text
218         t.executed()
219         t.postprocess()
220
221         t = tm.next_task()
222         t.prepare()
223         t.execute()
224         assert built_text == "n3 built", built_text
225         t.executed()
226         t.postprocess()
227
228         assert tm.next_task() == None
229
230         built_text = "up to date: "
231         top_node = n3
232
233         class MyTask(SCons.Taskmaster.Task):
234             def execute(self):
235                 global built_text
236                 if self.targets[0].get_state() == SCons.Node.up_to_date:
237                     if self.top:
238                         built_text = self.targets[0].name + " up-to-date top"
239                     else:
240                         built_text = self.targets[0].name + " up-to-date"
241                 else:
242                     self.targets[0].build()
243
244         n1.set_state(SCons.Node.no_state)
245         n1._current_val = 1
246         n2.set_state(SCons.Node.no_state)
247         n2._current_val = 1
248         n3.set_state(SCons.Node.no_state)
249         n3._current_val = 1
250         tm = SCons.Taskmaster.Taskmaster(targets = [n3], tasker = MyTask)
251
252         t = tm.next_task()
253         t.prepare()
254         t.execute()
255         assert built_text == "n1 up-to-date", built_text
256         t.executed()
257         t.postprocess()
258
259         t = tm.next_task()
260         t.prepare()
261         t.execute()
262         assert built_text == "n2 up-to-date", built_text
263         t.executed()
264         t.postprocess()
265
266         t = tm.next_task()
267         t.prepare()
268         t.execute()
269         assert built_text == "n3 up-to-date top", built_text
270         t.executed()
271         t.postprocess()
272
273         assert tm.next_task() == None
274
275
276         n1 = Node("n1")
277         n2 = Node("n2")
278         n3 = Node("n3", [n1, n2])
279         n4 = Node("n4")
280         n5 = Node("n5", [n3, n4])
281         tm = SCons.Taskmaster.Taskmaster([n5])
282
283         t1 = tm.next_task()
284         assert t1.get_target() == n1
285
286         t2 = tm.next_task()
287         assert t2.get_target() == n2
288
289         t4 = tm.next_task()
290         assert t4.get_target() == n4
291         t4.executed()
292         t4.postprocess()
293
294         t1.executed()
295         t1.postprocess()
296         t2.executed()
297         t2.postprocess()
298         t3 = tm.next_task()
299         assert t3.get_target() == n3
300
301         t3.executed()
302         t3.postprocess()
303         t5 = tm.next_task()
304         assert t5.get_target() == n5, t5.get_target()
305         t5.executed()
306         t5.postprocess()
307
308         assert tm.next_task() == None
309
310
311         n4 = Node("n4")
312         n4.set_state(SCons.Node.executed)
313         tm = SCons.Taskmaster.Taskmaster([n4])
314         assert tm.next_task() == None
315
316         n1 = Node("n1")
317         n2 = Node("n2", [n1])
318         tm = SCons.Taskmaster.Taskmaster([n2,n2])
319         t = tm.next_task()
320         t.executed()
321         t.postprocess()
322         t = tm.next_task()
323         assert tm.next_task() == None
324
325
326         n1 = Node("n1")
327         n2 = Node("n2")
328         n3 = Node("n3", [n1], [n2])
329         tm = SCons.Taskmaster.Taskmaster([n3])
330         t = tm.next_task()
331         target = t.get_target()
332         assert target == n1, target
333         t.executed()
334         t.postprocess()
335         t = tm.next_task()
336         target = t.get_target()
337         assert target == n2, target
338         t.executed()
339         t.postprocess()
340         t = tm.next_task()
341         target = t.get_target()
342         assert target == n3, target
343         t.executed()
344         t.postprocess()
345         assert tm.next_task() == None
346
347         n1 = Node("n1")
348         n2 = Node("n2")
349         n3 = Node("n3", [n1, n2])
350         n4 = Node("n4", [n3])
351         n5 = Node("n5", [n3])
352         global scan_called
353         scan_called = 0
354         tm = SCons.Taskmaster.Taskmaster([n4])
355         t = tm.next_task()
356         assert t.get_target() == n1
357         t.executed()
358         t.postprocess()
359         t = tm.next_task()
360         assert t.get_target() == n2
361         t.executed()
362         t.postprocess()
363         t = tm.next_task()
364         assert t.get_target() == n3
365         t.executed()
366         t.postprocess()
367         t = tm.next_task()
368         assert t.get_target() == n4
369         t.executed()
370         t.postprocess()
371         assert tm.next_task() == None
372         assert scan_called == 4, scan_called
373
374         tm = SCons.Taskmaster.Taskmaster([n5])
375         t = tm.next_task()
376         assert t.get_target() == n5, t.get_target()
377         t.executed()
378         assert tm.next_task() == None
379         assert scan_called == 5, scan_called
380
381         n1 = Node("n1")
382         n2 = Node("n2")
383         n3 = Node("n3")
384         n4 = Node("n4", [n1,n2,n3])
385         n5 = Node("n5", [n4])
386         n3.side_effect = 1
387         n1.side_effects = n2.side_effects = n3.side_effects = [n4]
388         tm = SCons.Taskmaster.Taskmaster([n1,n2,n3,n4,n5])
389         t = tm.next_task()
390         assert t.get_target() == n1
391         assert n4.state == SCons.Node.executing, n4.state
392         t.executed()
393         t.postprocess()
394         t = tm.next_task()
395         assert t.get_target() == n2
396         t.executed()
397         t.postprocess()
398         t = tm.next_task()
399         assert t.get_target() == n3
400         t.executed()
401         t.postprocess()
402         t = tm.next_task()
403         assert t.get_target() == n4
404         t.executed()
405         t.postprocess()
406         t = tm.next_task()
407         assert t.get_target() == n5
408         assert not tm.next_task()
409         t.executed()
410         t.postprocess()
411
412         n1 = Node("n1")
413         n2 = Node("n2")
414         n3 = Node("n3")
415         n4 = Node("n4", [n1,n2,n3])
416         def reverse(dependencies):
417             dependencies.reverse()
418             return dependencies
419         tm = SCons.Taskmaster.Taskmaster([n4], order=reverse)
420         t = tm.next_task()
421         assert t.get_target() == n3, t.get_target()
422         t.executed()
423         t.postprocess()
424         t = tm.next_task()
425         assert t.get_target() == n2, t.get_target()
426         t.executed()
427         t.postprocess()
428         t = tm.next_task()
429         assert t.get_target() == n1, t.get_target()
430         t.executed()
431         t.postprocess()
432         t = tm.next_task()
433         assert t.get_target() == n4, t.get_target()
434         t.executed()
435         t.postprocess()
436
437         n5 = Node("n5")
438         n6 = Node("n6")
439         n7 = Node("n7")
440         n6.alttargets = [n7]
441
442         tm = SCons.Taskmaster.Taskmaster([n5])
443         t = tm.next_task()
444         assert t.get_target() == n5
445         t.executed()
446         t.postprocess()
447
448         tm = SCons.Taskmaster.Taskmaster([n6])
449         t = tm.next_task()
450         assert t.get_target() == n7
451         t.executed()
452         t.postprocess()
453         t = tm.next_task()
454         assert t.get_target() == n6
455         t.executed()
456         t.postprocess()
457
458         n1 = Node("n1")
459         n2 = Node("n2", [n1])
460         n1.set_state(SCons.Node.failed)
461         tm = SCons.Taskmaster.Taskmaster([n2])
462         assert tm.next_task() is None
463
464         n1 = Node("n1")
465         n2 = Node("n2")
466         n1.targets = [n1, n2]
467         n1._current_val = 1
468         tm = SCons.Taskmaster.Taskmaster([n1])
469         t = tm.next_task()
470         t.executed()
471         t.postprocess()
472
473         s = n1.get_state()
474         assert s == SCons.Node.up_to_date, s
475         s = n2.get_state()
476         assert s == SCons.Node.executed, s
477
478
479     def test_make_ready_out_of_date(self):
480         """Test the Task.make_ready() method's list of out-of-date Nodes
481         """
482         ood = []
483         def TaskGen(tm, targets, top, node, ood=ood):
484             class MyTask(SCons.Taskmaster.Task):
485                 def make_ready(self):
486                     SCons.Taskmaster.Task.make_ready(self)
487                     self.ood.extend(self.out_of_date)
488             t = MyTask(tm, targets, top, node)
489             t.ood = ood
490             return t
491
492         n1 = Node("n1")
493         c2 = Node("c2")
494         c2._current_val = 1
495         n3 = Node("n3")
496         c4 = Node("c4")
497         c4._current_val = 1
498         tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4],
499                                          tasker = TaskGen)
500
501         del ood[:]
502         t = tm.next_task()
503         assert ood == [n1], ood
504
505         del ood[:]
506         t = tm.next_task()
507         assert ood == [], ood
508
509         del ood[:]
510         t = tm.next_task()
511         assert ood == [n3], ood
512
513         del ood[:]
514         t = tm.next_task()
515         assert ood == [], ood
516
517
518     def test_make_ready_exception(self):
519         """Test handling exceptions from Task.make_ready()
520         """
521         class MyTask(SCons.Taskmaster.Task):
522             def make_ready(self):
523                 raise MyException, "from make_ready()"
524
525         n1 = Node("n1")
526         tm = SCons.Taskmaster.Taskmaster(targets = [n1], tasker = MyTask)
527         t = tm.next_task()
528         exc_type, exc_value, exc_tb = t.exception
529         assert exc_type == MyException, repr(exc_type)
530         assert str(exc_value) == "from make_ready()", exc_value
531
532
533     def test_make_ready_all(self):
534         """Test the make_ready_all() method"""
535         class MyTask(SCons.Taskmaster.Task):
536             make_ready = SCons.Taskmaster.Task.make_ready_all
537
538         n1 = Node("n1")
539         c2 = Node("c2")
540         c2._current_val = 1
541         n3 = Node("n3")
542         c4 = Node("c4")
543         c4._current_val = 1
544
545         tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4])
546
547         t = tm.next_task()
548         target = t.get_target()
549         assert target is n1, target
550         assert target.state == SCons.Node.executing, target.state
551         t = tm.next_task()
552         target = t.get_target()
553         assert target is c2, target
554         assert target.state == SCons.Node.up_to_date, target.state
555         t = tm.next_task()
556         target = t.get_target()
557         assert target is n3, target
558         assert target.state == SCons.Node.executing, target.state
559         t = tm.next_task()
560         target = t.get_target()
561         assert target is c4, target
562         assert target.state == SCons.Node.up_to_date, target.state
563         t = tm.next_task()
564         assert t is None
565
566         n1 = Node("n1")
567         c2 = Node("c2")
568         n3 = Node("n3")
569         c4 = Node("c4")
570
571         tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4],
572                                          tasker = MyTask)
573
574         t = tm.next_task()
575         target = t.get_target()
576         assert target is n1, target
577         assert target.state == SCons.Node.executing, target.state
578         t = tm.next_task()
579         target = t.get_target()
580         assert target is c2, target
581         assert target.state == SCons.Node.executing, target.state
582         t = tm.next_task()
583         target = t.get_target()
584         assert target is n3, target
585         assert target.state == SCons.Node.executing, target.state
586         t = tm.next_task()
587         target = t.get_target()
588         assert target is c4, target
589         assert target.state == SCons.Node.executing, target.state
590         t = tm.next_task()
591         assert t is None
592
593
594     def test_children_errors(self):
595         """Test errors when fetching the children of a node.
596         """
597         class StopNode(Node):
598             def children(self):
599                 raise SCons.Errors.StopError, "stop!"
600         class ExitNode(Node):
601             def children(self):
602                 sys.exit(77)
603
604         n1 = StopNode("n1")
605         tm = SCons.Taskmaster.Taskmaster([n1])
606         t = tm.next_task()
607         exc_type, exc_value, exc_tb = t.exception
608         assert exc_type == SCons.Errors.StopError, repr(exc_type)
609         assert str(exc_value) == "stop!", exc_value
610
611         n2 = ExitNode("n2")
612         tm = SCons.Taskmaster.Taskmaster([n2])
613         t = tm.next_task()
614         exc_type, exc_value = t.exception
615         assert exc_type == SCons.Errors.ExplicitExit, repr(exc_type)
616         assert exc_value.node == n2, exc_value.node
617         assert exc_value.status == 77, exc_value.status
618
619     def test_cycle_detection(self):
620         """Test detecting dependency cycles
621         """
622         n1 = Node("n1")
623         n2 = Node("n2", [n1])
624         n3 = Node("n3", [n2])
625         n1.kids = [n3]
626
627         tm = SCons.Taskmaster.Taskmaster([n3])
628         try:
629             t = tm.next_task()
630         except SCons.Errors.UserError, e:
631             assert str(e) == "Dependency cycle: n3 -> n1 -> n2 -> n3", str(e)
632         else:
633             assert 'Did not catch expected UserError'
634
635     def test_next_top_level_candidate(self):
636         """Test the next_top_level_candidate() method
637         """
638         n1 = Node("n1")
639         n2 = Node("n2", [n1])
640         n3 = Node("n3", [n2])
641
642         tm = SCons.Taskmaster.Taskmaster([n3])
643         t = tm.next_task()
644         assert t.targets == [n1], t.targets
645         t.fail_stop()
646         assert t.targets == [n3], map(str, t.targets)
647         assert t.top == 1, t.top
648
649     def test_stop(self):
650         """Test the stop() method
651
652         Both default and overridden in a subclass.
653         """
654         global built_text
655
656         n1 = Node("n1")
657         n2 = Node("n2")
658         n3 = Node("n3", [n1, n2])
659
660         tm = SCons.Taskmaster.Taskmaster([n3])
661         t = tm.next_task()
662         t.prepare()
663         t.execute()
664         assert built_text == "n1 built", built_text
665         t.executed()
666         t.postprocess()
667         assert built_text == "n1 built really", built_text
668
669         tm.stop()
670         assert tm.next_task() is None
671
672         class MyTM(SCons.Taskmaster.Taskmaster):
673             def stop(self):
674                 global built_text
675                 built_text = "MyTM.stop()"
676                 SCons.Taskmaster.Taskmaster.stop(self)
677
678         n1 = Node("n1")
679         n2 = Node("n2")
680         n3 = Node("n3", [n1, n2])
681
682         built_text = None
683         tm = MyTM([n3])
684         tm.next_task().execute()
685         assert built_text == "n1 built"
686
687         tm.stop()
688         assert built_text == "MyTM.stop()"
689         assert tm.next_task() is None
690
691     def test_executed(self):
692         """Test when a task has been executed
693         """
694         global built_text
695         global visited_nodes
696
697         n1 = Node("n1")
698         tm = SCons.Taskmaster.Taskmaster([n1])
699         t = tm.next_task()
700         built_text = "xxx"
701         visited_nodes = []
702         n1.set_state(SCons.Node.executing)
703
704         t.executed()
705
706         s = n1.get_state()
707         assert s == SCons.Node.executed, s
708         assert built_text == "xxx really", built_text
709         assert visited_nodes == [], visited_nodes
710
711         n2 = Node("n2")
712         tm = SCons.Taskmaster.Taskmaster([n2])
713         t = tm.next_task()
714         built_text = "should_not_change"
715         visited_nodes = []
716         n2.set_state(None)
717
718         t.executed()
719
720         s = n2.get_state()
721         assert s is None, s
722         assert built_text == "should_not_change", built_text
723         assert visited_nodes == ['n2'], visited_nodes
724
725         n3 = Node("n3")
726         n4 = Node("n4")
727         n3.targets = [n3, n4]
728         tm = SCons.Taskmaster.Taskmaster([n3])
729         t = tm.next_task()
730         visited_nodes = []
731         n3.set_state(SCons.Node.up_to_date)
732         n4.set_state(SCons.Node.executing)
733
734         t.executed()
735
736         s = n3.get_state()
737         assert s == SCons.Node.up_to_date, s
738         s = n4.get_state()
739         assert s == SCons.Node.executed, s
740         assert visited_nodes == ['n3'], visited_nodes
741
742     def test_prepare(self):
743         """Test preparation of multiple Nodes for a task
744         """
745         n1 = Node("n1")
746         n2 = Node("n2")
747         tm = SCons.Taskmaster.Taskmaster([n1, n2])
748         t = tm.next_task()
749         # This next line is moderately bogus.  We're just reaching
750         # in and setting the targets for this task to an array.  The
751         # "right" way to do this would be to have the next_task() call
752         # set it up by having something that approximates a real Builder
753         # return this list--but that's more work than is probably
754         # warranted right now.
755         t.targets = [n1, n2]
756         t.prepare()
757         assert n1.prepared
758         assert n2.prepared
759
760         n3 = Node("n3")
761         n4 = Node("n4")
762         tm = SCons.Taskmaster.Taskmaster([n3, n4])
763         t = tm.next_task()
764         # More bogus reaching in and setting the targets.
765         n3.set_state(SCons.Node.up_to_date)
766         t.targets = [n3, n4]
767         t.prepare()
768         assert n3.prepared
769         assert n4.prepared
770
771         # If the Node has had an exception recorded while it was getting
772         # prepared, then prepare() should raise that exception.
773         class MyException(Exception):
774             pass
775
776         built_text = None
777         n5 = Node("n5")
778         tm = SCons.Taskmaster.Taskmaster([n5])
779         t = tm.next_task()
780         t.exception_set((MyException, "exception value"))
781         exc_caught = None
782         try:
783             t.prepare()
784         except MyException, e:
785             exc_caught = 1
786         except:
787             pass
788         assert exc_caught, "did not catch expected MyException"
789         assert str(e) == "exception value", e
790         assert built_text is None, built_text
791
792         # Regression test, make sure we prepare not only
793         # all targets, but their side effects as well.
794         n6 = Node("n6")
795         n7 = Node("n7")
796         n8 = Node("n8")
797         n9 = Node("n9")
798         n10 = Node("n10")
799
800         n6.side_effects = [ n8 ]
801         n7.side_effects = [ n9, n10 ]
802
803         tm = SCons.Taskmaster.Taskmaster([n6, n7])
804         t = tm.next_task()
805         # More bogus reaching in and setting the targets.
806         t.targets = [n6, n7]
807         t.prepare()
808         assert n6.prepared
809         assert n7.prepared
810         assert n8.prepared
811         assert n9.prepared
812         assert n10.prepared
813
814     def test_execute(self):
815         """Test executing a task
816         """
817         global built_text
818         global cache_text
819
820         n1 = Node("n1")
821         tm = SCons.Taskmaster.Taskmaster([n1])
822         t = tm.next_task()
823         t.execute()
824         assert built_text == "n1 built", built_text
825
826         def raise_UserError():
827             raise SCons.Errors.UserError
828         n2 = Node("n2")
829         n2.build = raise_UserError
830         tm = SCons.Taskmaster.Taskmaster([n2])
831         t = tm.next_task()
832         try:
833             t.execute()
834         except SCons.Errors.UserError:
835             pass
836         else:
837             raise TestFailed, "did not catch expected UserError"
838
839         def raise_BuildError():
840             raise SCons.Errors.BuildError
841         n3 = Node("n3")
842         n3.build = raise_BuildError
843         tm = SCons.Taskmaster.Taskmaster([n3])
844         t = tm.next_task()
845         try:
846             t.execute()
847         except SCons.Errors.BuildError:
848             pass
849         else:
850             raise TestFailed, "did not catch expected BuildError"
851
852         # On a generic (non-BuildError) exception from a Builder,
853         # the target should throw a BuildError exception with the
854         # args set to the exception value, instance, and traceback.
855         def raise_OtherError():
856             raise OtherError
857         n4 = Node("n4")
858         n4.build = raise_OtherError
859         tm = SCons.Taskmaster.Taskmaster([n4])
860         t = tm.next_task()
861         try:
862             t.execute()
863         except SCons.Errors.TaskmasterException, e:
864             assert e.node == n4, e.node
865             assert e.errstr == "Exception", e.errstr
866             assert len(e.exc_info) == 3, e.exc_info
867             exc_traceback = sys.exc_info()[2]
868             assert type(e.exc_info[2]) == type(exc_traceback), e.exc_info[2]
869         else:
870             raise TestFailed, "did not catch expected BuildError"
871
872         built_text = None
873         cache_text = []
874         n5 = Node("n5")
875         n6 = Node("n6")
876         n6.cached = 1
877         tm = SCons.Taskmaster.Taskmaster([n5])
878         t = tm.next_task()
879         # This next line is moderately bogus.  We're just reaching
880         # in and setting the targets for this task to an array.  The
881         # "right" way to do this would be to have the next_task() call
882         # set it up by having something that approximates a real Builder
883         # return this list--but that's more work than is probably
884         # warranted right now.
885         t.targets = [n5, n6]
886         t.execute()
887         assert built_text == "n5 built", built_text
888         assert cache_text == [], cache_text
889
890         built_text = None
891         cache_text = []
892         n7 = Node("n7")
893         n8 = Node("n8")
894         n7.cached = 1
895         n8.cached = 1
896         tm = SCons.Taskmaster.Taskmaster([n7])
897         t = tm.next_task()
898         # This next line is moderately bogus.  We're just reaching
899         # in and setting the targets for this task to an array.  The
900         # "right" way to do this would be to have the next_task() call
901         # set it up by having something that approximates a real Builder
902         # return this list--but that's more work than is probably
903         # warranted right now.
904         t.targets = [n7, n8]
905         t.execute()
906         assert built_text is None, built_text
907         assert cache_text == ["n7 retrieved", "n8 retrieved"], cache_text
908
909     def test_exception(self):
910         """Test generic Taskmaster exception handling
911
912         """
913         n1 = Node("n1")
914         tm = SCons.Taskmaster.Taskmaster([n1])
915         t  = tm.next_task()
916
917         t.exception_set((1, 2))
918         exc_type, exc_value = t.exception
919         assert exc_type == 1, exc_type
920         assert exc_value == 2, exc_value
921
922         t.exception_set(3)
923         assert t.exception == 3
924
925         try: 1/0
926         except: pass
927         t.exception_set(None)
928         exc_type, exc_value, exc_tb = t.exception
929         assert exc_type is ZeroDivisionError, exc_type
930         exception_values = [
931             "integer division or modulo",
932             "integer division or modulo by zero",
933         ]
934         assert str(exc_value) in exception_values, exc_value
935
936         t.exception_set(("exception 1", None))
937         try:
938             t.exception_raise()
939         except:
940             exc_type, exc_value = sys.exc_info()[:2]
941             assert exc_type == "exception 1", exc_type
942             assert exc_value is None, exc_value
943         else:
944             assert 0, "did not catch expected exception"
945
946         t.exception_set(("exception 2", "xyzzy"))
947         try:
948             t.exception_raise()
949         except:
950             exc_type, exc_value = sys.exc_info()[:2]
951             assert exc_type == "exception 2", exc_type
952             assert exc_value == "xyzzy", exc_value
953         else:
954             assert 0, "did not catch expected exception"
955
956         try:
957             1/0
958         except:
959             tb = sys.exc_info()[2]
960         t.exception_set(("exception 3", "arg", tb))
961         try:
962             t.exception_raise()
963         except:
964             exc_type, exc_value, exc_tb = sys.exc_info()
965             assert exc_type == 'exception 3', exc_type
966             assert exc_value == "arg", exc_value
967             import traceback
968             x = traceback.extract_tb(tb)[-1]
969             y = traceback.extract_tb(exc_tb)[-1]
970             assert x == y, "x = %s, y = %s" % (x, y)
971         else:
972             assert 0, "did not catch expected exception"
973
974     def test_postprocess(self):
975         """Test postprocessing targets to give them a chance to clean up
976         """
977         n1 = Node("n1")
978         tm = SCons.Taskmaster.Taskmaster([n1])
979
980         t = tm.next_task()
981         assert not n1.postprocessed
982         t.postprocess()
983         assert n1.postprocessed
984
985         n2 = Node("n2")
986         n3 = Node("n3")
987         tm = SCons.Taskmaster.Taskmaster([n2, n3])
988
989         assert not n2.postprocessed
990         assert not n3.postprocessed
991         t = tm.next_task()
992         t.postprocess()
993         assert n2.postprocessed
994         assert not n3.postprocessed
995         t = tm.next_task()
996         t.postprocess()
997         assert n2.postprocessed
998         assert n3.postprocessed
999
1000     def test_trace(self):
1001         """Test Taskmaster tracing
1002         """
1003         import StringIO
1004
1005         trace = StringIO.StringIO()
1006         n1 = Node("n1")
1007         n2 = Node("n2")
1008         n3 = Node("n3", [n1, n2])
1009         tm = SCons.Taskmaster.Taskmaster([n1, n1, n3], trace=trace)
1010         t = tm.next_task()
1011         t.prepare()
1012         t.execute()
1013         n1.set_state(SCons.Node.executed)
1014         t = tm.next_task()
1015         t.prepare()
1016         t.execute()
1017         t = tm.next_task()
1018
1019         value = trace.getvalue()
1020         expect = """\
1021 Taskmaster: 'n1': evaluating n1
1022 Taskmaster: 'n1': already handled (executed)
1023 Taskmaster: 'n3': children:
1024     ['n1', 'n2']
1025     waiting on unstarted children:
1026     ['n2']
1027 Taskmaster: 'n2': evaluating n2
1028 Taskmaster: 'n3': children:
1029     ['n1', 'n2']
1030     waiting on unfinished children:
1031     ['n2']
1032 """
1033         assert value == expect, value
1034
1035
1036
1037 if __name__ == "__main__":
1038     suite = unittest.makeSuite(TaskmasterTestCase, 'test_')
1039     if not unittest.TextTestRunner().run(suite).wasSuccessful():
1040         sys.exit(1)