Evaluate compat module transparently when SCons is instantiated
[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         self.prerequisites = []
50         class Builder:
51             def targets(self, node):
52                 return node.targets
53         self.builder = Builder()
54         self.bsig = None
55         self.csig = None
56         self.state = SCons.Node.no_state
57         self.prepared = None
58         self.ref_count = 0
59         self.waiting_parents = set()
60         self.waiting_s_e = set()
61         self.side_effect = 0
62         self.side_effects = []
63         self.alttargets = []
64         self.postprocessed = None
65         self._bsig_val = None
66         self._current_val = 0
67         self.always_build = None
68
69     def disambiguate(self):
70         return self
71
72     def retrieve_from_cache(self):
73         global cache_text
74         if self.cached:
75             cache_text.append(self.name + " retrieved")
76         return self.cached
77
78     def make_ready(self):
79         pass
80
81     def prepare(self):
82         self.prepared = 1
83
84     def build(self):
85         global built_text
86         built_text = self.name + " built"
87
88     def built(self):
89         global built_text
90         built_text = built_text + " really"
91
92     def has_builder(self):
93         return not self.builder is None
94
95     def is_derived(self):
96         return self.has_builder or self.side_effect
97
98     def alter_targets(self):
99         return self.alttargets, None
100
101     def visited(self):
102         global visited_nodes
103         visited_nodes.append(self.name)
104
105     def children(self):
106         if not self.scanned:
107             self.scan()
108             self.scanned = 1
109         return self.kids
110
111     def scan(self):
112         global scan_called
113         scan_called = scan_called + 1
114         self.kids = self.kids + self.scans
115         self.scans = []
116
117     def scanner_key(self):
118         return self.name
119
120     def add_to_waiting_parents(self, node):
121         wp = self.waiting_parents
122         if node in wp:
123             return 0
124         wp.add(node)
125         return 1
126
127     def get_state(self):
128         return self.state
129
130     def set_state(self, state):
131         self.state = state
132
133     def set_bsig(self, bsig):
134         self.bsig = bsig
135
136     def set_csig(self, csig):
137         self.csig = csig
138
139     def store_csig(self):
140         pass
141
142     def store_bsig(self):
143         pass
144
145     def is_pseudo_derived(self):
146         pass
147
148     def is_up_to_date(self):
149         return self._current_val
150     
151     def depends_on(self, nodes):
152         for node in nodes:
153             if node in self.kids:
154                 return 1
155         return 0
156
157     def __str__(self):
158         return self.name
159
160     def postprocess(self):
161         self.postprocessed = 1
162         self.waiting_parents = set()
163
164     def get_executor(self):
165         if not hasattr(self, 'executor'):
166             class Executor:
167                 def prepare(self):
168                     pass
169             self.executor = Executor()
170         self.executor.targets = self.targets
171         return self.executor
172
173 class OtherError(Exception):
174     pass
175
176 class MyException(Exception):
177     pass
178
179
180 class TaskmasterTestCase(unittest.TestCase):
181
182     def test_next_task(self):
183         """Test fetching the next task
184         """
185         global built_text
186
187         n1 = Node("n1")
188         tm = SCons.Taskmaster.Taskmaster([n1, n1])
189         t = tm.next_task()
190         t.prepare()
191         t.execute()
192         t = tm.next_task()
193         assert t == None
194
195         n1 = Node("n1")
196         n2 = Node("n2")
197         n3 = Node("n3", [n1, n2])
198
199         tm = SCons.Taskmaster.Taskmaster([n3])
200
201         t = tm.next_task()
202         t.prepare()
203         t.execute()
204         assert built_text == "n1 built", built_text
205         t.executed()
206         t.postprocess()
207
208         t = tm.next_task()
209         t.prepare()
210         t.execute()
211         assert built_text == "n2 built", built_text
212         t.executed()
213         t.postprocess()
214
215         t = tm.next_task()
216         t.prepare()
217         t.execute()
218         assert built_text == "n3 built", built_text
219         t.executed()
220         t.postprocess()
221
222         assert tm.next_task() == None
223
224         built_text = "up to date: "
225         top_node = n3
226
227         class MyTask(SCons.Taskmaster.Task):
228             def execute(self):
229                 global built_text
230                 if self.targets[0].get_state() == SCons.Node.up_to_date:
231                     if self.top:
232                         built_text = self.targets[0].name + " up-to-date top"
233                     else:
234                         built_text = self.targets[0].name + " up-to-date"
235                 else:
236                     self.targets[0].build()
237
238         n1.set_state(SCons.Node.no_state)
239         n1._current_val = 1
240         n2.set_state(SCons.Node.no_state)
241         n2._current_val = 1
242         n3.set_state(SCons.Node.no_state)
243         n3._current_val = 1
244         tm = SCons.Taskmaster.Taskmaster(targets = [n3], tasker = MyTask)
245
246         t = tm.next_task()
247         t.prepare()
248         t.execute()
249         assert built_text == "n1 up-to-date", built_text
250         t.executed()
251         t.postprocess()
252
253         t = tm.next_task()
254         t.prepare()
255         t.execute()
256         assert built_text == "n2 up-to-date", built_text
257         t.executed()
258         t.postprocess()
259
260         t = tm.next_task()
261         t.prepare()
262         t.execute()
263         assert built_text == "n3 up-to-date top", built_text
264         t.executed()
265         t.postprocess()
266
267         assert tm.next_task() == None
268
269
270         n1 = Node("n1")
271         n2 = Node("n2")
272         n3 = Node("n3", [n1, n2])
273         n4 = Node("n4")
274         n5 = Node("n5", [n3, n4])
275         tm = SCons.Taskmaster.Taskmaster([n5])
276
277         t1 = tm.next_task()
278         assert t1.get_target() == n1
279
280         t2 = tm.next_task()
281         assert t2.get_target() == n2
282
283         t4 = tm.next_task()
284         assert t4.get_target() == n4
285         t4.executed()
286         t4.postprocess()
287
288         t1.executed()
289         t1.postprocess()
290         t2.executed()
291         t2.postprocess()
292         t3 = tm.next_task()
293         assert t3.get_target() == n3
294
295         t3.executed()
296         t3.postprocess()
297         t5 = tm.next_task()
298         assert t5.get_target() == n5, t5.get_target()
299         t5.executed()
300         t5.postprocess()
301
302         assert tm.next_task() == None
303
304
305         n4 = Node("n4")
306         n4.set_state(SCons.Node.executed)
307         tm = SCons.Taskmaster.Taskmaster([n4])
308         assert tm.next_task() == None
309
310         n1 = Node("n1")
311         n2 = Node("n2", [n1])
312         tm = SCons.Taskmaster.Taskmaster([n2,n2])
313         t = tm.next_task()
314         t.executed()
315         t.postprocess()
316         t = tm.next_task()
317         assert tm.next_task() == None
318
319
320         n1 = Node("n1")
321         n2 = Node("n2")
322         n3 = Node("n3", [n1], [n2])
323         tm = SCons.Taskmaster.Taskmaster([n3])
324         t = tm.next_task()
325         target = t.get_target()
326         assert target == n1, target
327         t.executed()
328         t.postprocess()
329         t = tm.next_task()
330         target = t.get_target()
331         assert target == n2, target
332         t.executed()
333         t.postprocess()
334         t = tm.next_task()
335         target = t.get_target()
336         assert target == n3, target
337         t.executed()
338         t.postprocess()
339         assert tm.next_task() == None
340
341         n1 = Node("n1")
342         n2 = Node("n2")
343         n3 = Node("n3", [n1, n2])
344         n4 = Node("n4", [n3])
345         n5 = Node("n5", [n3])
346         global scan_called
347         scan_called = 0
348         tm = SCons.Taskmaster.Taskmaster([n4])
349         t = tm.next_task()
350         assert t.get_target() == n1
351         t.executed()
352         t.postprocess()
353         t = tm.next_task()
354         assert t.get_target() == n2
355         t.executed()
356         t.postprocess()
357         t = tm.next_task()
358         assert t.get_target() == n3
359         t.executed()
360         t.postprocess()
361         t = tm.next_task()
362         assert t.get_target() == n4
363         t.executed()
364         t.postprocess()
365         assert tm.next_task() == None
366         assert scan_called == 4, scan_called
367
368         tm = SCons.Taskmaster.Taskmaster([n5])
369         t = tm.next_task()
370         assert t.get_target() == n5, t.get_target()
371         t.executed()
372         assert tm.next_task() == None
373         assert scan_called == 5, scan_called
374
375         n1 = Node("n1")
376         n2 = Node("n2")
377         n3 = Node("n3")
378         n4 = Node("n4", [n1,n2,n3])
379         n5 = Node("n5", [n4])
380         n3.side_effect = 1
381         n1.side_effects = n2.side_effects = n3.side_effects = [n4]
382         tm = SCons.Taskmaster.Taskmaster([n1,n2,n3,n4,n5])
383         t = tm.next_task()
384         assert t.get_target() == n1
385         assert n4.state == SCons.Node.executing, n4.state
386         t.executed()
387         t.postprocess()
388         t = tm.next_task()
389         assert t.get_target() == n2
390         t.executed()
391         t.postprocess()
392         t = tm.next_task()
393         assert t.get_target() == n3
394         t.executed()
395         t.postprocess()
396         t = tm.next_task()
397         assert t.get_target() == n4
398         t.executed()
399         t.postprocess()
400         t = tm.next_task()
401         assert t.get_target() == n5
402         assert not tm.next_task()
403         t.executed()
404         t.postprocess()
405
406         n1 = Node("n1")
407         n2 = Node("n2")
408         n3 = Node("n3")
409         n4 = Node("n4", [n1,n2,n3])
410         def reverse(dependencies):
411             dependencies.reverse()
412             return dependencies
413         tm = SCons.Taskmaster.Taskmaster([n4], order=reverse)
414         t = tm.next_task()
415         assert t.get_target() == n3, t.get_target()
416         t.executed()
417         t.postprocess()
418         t = tm.next_task()
419         assert t.get_target() == n2, t.get_target()
420         t.executed()
421         t.postprocess()
422         t = tm.next_task()
423         assert t.get_target() == n1, t.get_target()
424         t.executed()
425         t.postprocess()
426         t = tm.next_task()
427         assert t.get_target() == n4, t.get_target()
428         t.executed()
429         t.postprocess()
430
431         n5 = Node("n5")
432         n6 = Node("n6")
433         n7 = Node("n7")
434         n6.alttargets = [n7]
435
436         tm = SCons.Taskmaster.Taskmaster([n5])
437         t = tm.next_task()
438         assert t.get_target() == n5
439         t.executed()
440         t.postprocess()
441
442         tm = SCons.Taskmaster.Taskmaster([n6])
443         t = tm.next_task()
444         assert t.get_target() == n7
445         t.executed()
446         t.postprocess()
447         t = tm.next_task()
448         assert t.get_target() == n6
449         t.executed()
450         t.postprocess()
451
452         n1 = Node("n1")
453         n2 = Node("n2", [n1])
454         n1.set_state(SCons.Node.failed)
455         tm = SCons.Taskmaster.Taskmaster([n2])
456         assert tm.next_task() is None
457
458         n1 = Node("n1")
459         n2 = Node("n2")
460         n1.targets = [n1, n2]
461         n1._current_val = 1
462         tm = SCons.Taskmaster.Taskmaster([n1])
463         t = tm.next_task()
464         t.executed()
465         t.postprocess()
466
467         s = n1.get_state()
468         assert s == SCons.Node.executed, s
469         s = n2.get_state()
470         assert s == SCons.Node.executed, s
471
472
473     def test_make_ready_out_of_date(self):
474         """Test the Task.make_ready() method's list of out-of-date Nodes
475         """
476         ood = []
477         def TaskGen(tm, targets, top, node, ood=ood):
478             class MyTask(SCons.Taskmaster.Task):
479                 def make_ready(self):
480                     SCons.Taskmaster.Task.make_ready(self)
481                     self.ood.extend(self.out_of_date)
482             t = MyTask(tm, targets, top, node)
483             t.ood = ood
484             return t
485
486         n1 = Node("n1")
487         c2 = Node("c2")
488         c2._current_val = 1
489         n3 = Node("n3")
490         c4 = Node("c4")
491         c4._current_val = 1
492         a5 = Node("a5")
493         a5._current_val = 1
494         a5.always_build = 1
495         tm = SCons.Taskmaster.Taskmaster(targets = [n1, c2, n3, c4, a5],
496                                          tasker = TaskGen)
497
498         del ood[:]
499         t = tm.next_task()
500         assert ood == [n1], ood
501
502         del ood[:]
503         t = tm.next_task()
504         assert ood == [], ood
505
506         del ood[:]
507         t = tm.next_task()
508         assert ood == [n3], ood
509
510         del ood[:]
511         t = tm.next_task()
512         assert ood == [], ood
513
514         del ood[:]
515         t = tm.next_task()
516         assert ood == [a5], ood
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 == ['n1'], 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', 'n4'], 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         # Make sure we call an Executor's prepare() method.
815         class ExceptionExecutor:
816             def prepare(self):
817                 raise Exception, "Executor.prepare() exception"
818
819         n11 = Node("n11")
820         n11.executor = ExceptionExecutor()
821         tm = SCons.Taskmaster.Taskmaster([n11])
822         t = tm.next_task()
823         try:
824             t.prepare()
825         except Exception, e:
826             assert str(e) == "Executor.prepare() exception", e
827         else:
828             raise AssertionError, "did not catch expected exception"
829
830     def test_execute(self):
831         """Test executing a task
832         """
833         global built_text
834         global cache_text
835
836         n1 = Node("n1")
837         tm = SCons.Taskmaster.Taskmaster([n1])
838         t = tm.next_task()
839         t.execute()
840         assert built_text == "n1 built", built_text
841
842         def raise_UserError():
843             raise SCons.Errors.UserError
844         n2 = Node("n2")
845         n2.build = raise_UserError
846         tm = SCons.Taskmaster.Taskmaster([n2])
847         t = tm.next_task()
848         try:
849             t.execute()
850         except SCons.Errors.UserError:
851             pass
852         else:
853             raise TestFailed, "did not catch expected UserError"
854
855         def raise_BuildError():
856             raise SCons.Errors.BuildError
857         n3 = Node("n3")
858         n3.build = raise_BuildError
859         tm = SCons.Taskmaster.Taskmaster([n3])
860         t = tm.next_task()
861         try:
862             t.execute()
863         except SCons.Errors.BuildError:
864             pass
865         else:
866             raise TestFailed, "did not catch expected BuildError"
867
868         # On a generic (non-BuildError) exception from a Builder,
869         # the target should throw a BuildError exception with the
870         # args set to the exception value, instance, and traceback.
871         def raise_OtherError():
872             raise OtherError
873         n4 = Node("n4")
874         n4.build = raise_OtherError
875         tm = SCons.Taskmaster.Taskmaster([n4])
876         t = tm.next_task()
877         try:
878             t.execute()
879         except SCons.Errors.TaskmasterException, e:
880             assert e.node == n4, e.node
881             assert e.errstr == "Exception", e.errstr
882             assert len(e.exc_info) == 3, e.exc_info
883             exc_traceback = sys.exc_info()[2]
884             assert type(e.exc_info[2]) == type(exc_traceback), e.exc_info[2]
885         else:
886             raise TestFailed, "did not catch expected BuildError"
887
888         built_text = None
889         cache_text = []
890         n5 = Node("n5")
891         n6 = Node("n6")
892         n6.cached = 1
893         tm = SCons.Taskmaster.Taskmaster([n5])
894         t = tm.next_task()
895         # This next line is moderately bogus.  We're just reaching
896         # in and setting the targets for this task to an array.  The
897         # "right" way to do this would be to have the next_task() call
898         # set it up by having something that approximates a real Builder
899         # return this list--but that's more work than is probably
900         # warranted right now.
901         t.targets = [n5, n6]
902         t.execute()
903         assert built_text == "n5 built", built_text
904         assert cache_text == [], cache_text
905
906         built_text = None
907         cache_text = []
908         n7 = Node("n7")
909         n8 = Node("n8")
910         n7.cached = 1
911         n8.cached = 1
912         tm = SCons.Taskmaster.Taskmaster([n7])
913         t = tm.next_task()
914         # This next line is moderately bogus.  We're just reaching
915         # in and setting the targets for this task to an array.  The
916         # "right" way to do this would be to have the next_task() call
917         # set it up by having something that approximates a real Builder
918         # return this list--but that's more work than is probably
919         # warranted right now.
920         t.targets = [n7, n8]
921         t.execute()
922         assert built_text is None, built_text
923         assert cache_text == ["n7 retrieved", "n8 retrieved"], cache_text
924
925     def test_exception(self):
926         """Test generic Taskmaster exception handling
927
928         """
929         n1 = Node("n1")
930         tm = SCons.Taskmaster.Taskmaster([n1])
931         t  = tm.next_task()
932
933         t.exception_set((1, 2))
934         exc_type, exc_value = t.exception
935         assert exc_type == 1, exc_type
936         assert exc_value == 2, exc_value
937
938         t.exception_set(3)
939         assert t.exception == 3
940
941         try: 1/0
942         except: pass
943         t.exception_set(None)
944         exc_type, exc_value, exc_tb = t.exception
945         assert exc_type is ZeroDivisionError, exc_type
946         exception_values = [
947             "integer division or modulo",
948             "integer division or modulo by zero",
949         ]
950         assert str(exc_value) in exception_values, exc_value
951
952         class Exception1(Exception):
953             pass
954
955         t.exception_set((Exception1, None))
956         try:
957             t.exception_raise()
958         except:
959             exc_type, exc_value = sys.exc_info()[:2]
960             assert exc_type == Exception1, exc_type
961             assert str(exc_value) == '', exc_value
962         else:
963             assert 0, "did not catch expected exception"
964
965         class Exception2(Exception):
966             pass
967
968         t.exception_set((Exception2, "xyzzy"))
969         try:
970             t.exception_raise()
971         except:
972             exc_type, exc_value = sys.exc_info()[:2]
973             assert exc_type == Exception2, exc_type
974             assert str(exc_value) == "xyzzy", exc_value
975         else:
976             assert 0, "did not catch expected exception"
977
978         class Exception3(Exception):
979             pass
980
981         try:
982             1/0
983         except:
984             tb = sys.exc_info()[2]
985         t.exception_set((Exception3, "arg", tb))
986         try:
987             t.exception_raise()
988         except:
989             exc_type, exc_value, exc_tb = sys.exc_info()
990             assert exc_type == Exception3, exc_type
991             assert str(exc_value) == "arg", exc_value
992             import traceback
993             x = traceback.extract_tb(tb)[-1]
994             y = traceback.extract_tb(exc_tb)[-1]
995             assert x == y, "x = %s, y = %s" % (x, y)
996         else:
997             assert 0, "did not catch expected exception"
998
999     def test_postprocess(self):
1000         """Test postprocessing targets to give them a chance to clean up
1001         """
1002         n1 = Node("n1")
1003         tm = SCons.Taskmaster.Taskmaster([n1])
1004
1005         t = tm.next_task()
1006         assert not n1.postprocessed
1007         t.postprocess()
1008         assert n1.postprocessed
1009
1010         n2 = Node("n2")
1011         n3 = Node("n3")
1012         tm = SCons.Taskmaster.Taskmaster([n2, n3])
1013
1014         assert not n2.postprocessed
1015         assert not n3.postprocessed
1016         t = tm.next_task()
1017         t.postprocess()
1018         assert n2.postprocessed
1019         assert not n3.postprocessed
1020         t = tm.next_task()
1021         t.postprocess()
1022         assert n2.postprocessed
1023         assert n3.postprocessed
1024
1025     def test_trace(self):
1026         """Test Taskmaster tracing
1027         """
1028         import StringIO
1029
1030         trace = StringIO.StringIO()
1031         n1 = Node("n1")
1032         n2 = Node("n2")
1033         n3 = Node("n3", [n1, n2])
1034         tm = SCons.Taskmaster.Taskmaster([n1, n1, n3], trace=trace)
1035         t = tm.next_task()
1036         t.prepare()
1037         t.execute()
1038         t.postprocess()
1039         n1.set_state(SCons.Node.executed)
1040         t = tm.next_task()
1041         t.prepare()
1042         t.execute()
1043         t.postprocess()
1044         n2.set_state(SCons.Node.executed)
1045         t = tm.next_task()
1046         t.prepare()
1047         t.execute()
1048         t.postprocess()
1049         t = tm.next_task()
1050         assert t is None
1051
1052         value = trace.getvalue()
1053         expect = """\
1054
1055 Taskmaster: Looking for a node to evaluate
1056 Taskmaster:     Considering node <no_state   0   'n1'> and its children:
1057 Taskmaster: Evaluating <pending    0   'n1'>
1058
1059 Taskmaster: Looking for a node to evaluate
1060 Taskmaster:     Considering node <executed   0   'n1'> and its children:
1061 Taskmaster:        already handled (executed)
1062 Taskmaster:     Considering node <no_state   0   'n3'> and its children:
1063 Taskmaster:        <executed   0   'n1'>
1064 Taskmaster:        <no_state   0   'n2'>
1065 Taskmaster:      adjusting ref count: <pending    1   'n3'>
1066 Taskmaster:     Considering node <no_state   0   'n2'> and its children:
1067 Taskmaster: Evaluating <pending    0   'n2'>
1068
1069 Taskmaster: Looking for a node to evaluate
1070 Taskmaster:     Considering node <pending    0   'n3'> and its children:
1071 Taskmaster:        <executed   0   'n1'>
1072 Taskmaster:        <executed   0   'n2'>
1073 Taskmaster: Evaluating <pending    0   'n3'>
1074
1075 Taskmaster: Looking for a node to evaluate
1076 Taskmaster: No candidate anymore.
1077
1078 """
1079         assert value == expect, value
1080
1081
1082
1083 if __name__ == "__main__":
1084     suite = unittest.makeSuite(TaskmasterTestCase, 'test_')
1085     if not unittest.TextTestRunner().run(suite).wasSuccessful():
1086         sys.exit(1)