3 # Copyright The SCons Foundation
5 # Permission is hereby granted, free of charge, to any person obtaining
6 # a copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to
10 # permit persons to whom the Software is furnished to do so, subject to
11 # the following conditions:
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 import SCons
.cpp
as cpp
38 substitution_input
= """
39 #define FILE3 "file3-yes"
40 #define FILE4 <file4-yes>
45 #define XXX_FILE5 YYY_FILE5
46 #define YYY_FILE5 ZZZ_FILE5
47 #define ZZZ_FILE5 FILE5
49 #define FILE5 "file5-yes"
50 #define FILE6 <file6-yes>
52 #define XXX_FILE6 YYY_FILE6
53 #define YYY_FILE6 ZZZ_FILE6
54 #define ZZZ_FILE6 FILE6
59 #include SHELL_ESCAPED_H
66 #ifdef DEFINED /* multi-line comment */
80 if_boolean_input
= """
81 #define ZERO 0 // single-line comment
97 #include "file11-no-1"
99 #include "file11-no-2"
101 #include "file11-yes"
105 #include <file12-no-1>
107 #include <file12-yes>
109 #include <file12-no-2>
113 #include "file13-yes"
115 #include "file13-no-1"
117 #include "file13-no-2"
121 #include <file14-yes>
123 #include <file14-no-1>
125 #include <file14-no-2>
130 if_defined_input
= """
134 #if defined(DEFINED_A)
135 #include "file15-yes"
138 #if ! defined(DEFINED_A)
141 #include <file16-yes>
144 #if defined DEFINED_A
145 #include "file17-yes"
148 #if ! defined DEFINED_A
151 #include <file18-yes>
154 #if ! (defined (DEFINED_A) || defined (DEFINED_B)
157 #include <file19-yes>
163 expression_input
= """
170 #include "file19-yes"
176 #include <file20-yes>
182 #include "file21-yes"
186 #include <file22-yes>
194 #include "file23-yes"
198 #include <file24-yes>
204 #include "file25-yes"
210 #include <file26-yes>
216 #include "file27-yes"
224 #include <file28-yes>
230 #include "file29-yes"
234 #include <file30-yes>
239 #if 123456789UL || 0x13L
240 #include <file301-yes>
242 #include <file301-no>
251 #include "file31-yes"
261 #include <file32-yes>
266 macro_function_input
= """
270 #define FUNC33(x) "file33-yes"
271 #define FUNC34(x) <file34-yes>
273 #include FUNC33(ZERO)
274 #include FUNC34(ZERO)
276 #define FILE35 "file35-yes"
277 #define FILE36 <file36-yes>
279 #define FUNC35(x, y) FILE35
280 #define FUNC36(x, y) FILE36
282 #include FUNC35(ZERO, ONE)
283 #include FUNC36(ZERO, ONE)
285 #define FILE37 "file37-yes"
286 #define FILE38 <file38-yes>
288 #define FUNC37a(x, y) FILE37
289 #define FUNC38a(x, y) FILE38
291 #define FUNC37b(x, y) FUNC37a(x, y)
292 #define FUNC38b(x, y) FUNC38a(x, y)
294 #define FUNC37c(x, y) FUNC37b(x, y)
295 #define FUNC38c(x, y) FUNC38b(x, y)
297 #include FUNC37c(ZERO, ONE)
298 #include FUNC38c(ZERO, ONE)
300 #define FILE39 "file39-yes"
301 #define FILE40 <file40-yes>
303 #define FUNC39a(x0, y0) FILE39
304 #define FUNC40a(x0, y0) FILE40
306 #define FUNC39b(x1, y2) FUNC39a(x1, y1)
307 #define FUNC40b(x1, y2) FUNC40a(x1, y1)
309 #define FUNC39c(x2, y2) FUNC39b(x2, y2)
310 #define FUNC40c(x2, y2) FUNC40b(x2, y2)
312 #include FUNC39c(ZERO, ONE)
313 #include FUNC40c(ZERO, ONE)
315 /* Make sure we don't die if the expansion isn't a string. */
316 #define FUNC_INTEGER(x) 1
318 /* Make sure one-character names are recognized. */
319 #define _(x) translate(x)
324 token_pasting_input
= """
325 #define PASTE_QUOTE(q, name) q##name##-yes##q
326 #define PASTE_ANGLE(name) <##name##-yes>
328 #define FUNC41 PASTE_QUOTE(", file41)
329 #define FUNC42 PASTE_ANGLE(file42)
342 nested_ifs_input
= """
365 #include "file45-yes"
369 #include <file46-yes>
376 if_defined_no_space_input
= """
380 #include "file47-yes"
383 #if(!defined DEFINED)
385 #elif(!defined DEFINED)
388 #include <file50-yes>
391 #if!(defined DEFINED)
393 #elif!(defined DEFINED)
396 #include "file53-yes"
400 if_no_space_input
= """
408 #include <file55-yes>
416 #include <file58-yes>
420 #include "file59-yes"
428 #include <file62-yes>
433 # pp_class = PreProcessor
434 # #pp_class = DumbPreProcessor
436 # pp = pp_class(current = ".",
437 # cpppath = ['/usr/include'],
439 # #pp(open(sys.argv[1]).read())
443 class cppTestCase(unittest
.TestCase
):
444 def setUp(self
) -> None:
445 self
.cpp
= self
.cpp_class(current
= ".",
446 cpppath
= ['/usr/include'],
447 dict={"SHELL_ESCAPED_H": '\\"file-shell-computed-yes\\"'})
449 def test_basic(self
) -> None:
450 """Test basic #include scanning"""
451 expect
= self
.basic_expect
452 result
= self
.cpp
.process_contents(basic_input
)
453 assert expect
== result
, (expect
, result
)
455 def test_substitution(self
) -> None:
456 """Test substitution of #include files using CPP variables"""
457 expect
= self
.substitution_expect
458 result
= self
.cpp
.process_contents(substitution_input
)
459 assert expect
== result
, (expect
, result
)
461 def test_ifdef(self
) -> None:
462 """Test basic #ifdef processing"""
463 expect
= self
.ifdef_expect
464 result
= self
.cpp
.process_contents(ifdef_input
)
465 assert expect
== result
, (expect
, result
)
467 def test_if_boolean(self
) -> None:
468 """Test #if with Boolean values"""
469 expect
= self
.if_boolean_expect
470 result
= self
.cpp
.process_contents(if_boolean_input
)
471 assert expect
== result
, (expect
, result
)
473 def test_if_defined(self
) -> None:
474 """Test #if defined() idioms"""
475 expect
= self
.if_defined_expect
476 result
= self
.cpp
.process_contents(if_defined_input
)
477 assert expect
== result
, (expect
, result
)
479 def test_expression(self
) -> None:
480 """Test #if with arithmetic expressions"""
481 expect
= self
.expression_expect
482 result
= self
.cpp
.process_contents(expression_input
)
483 assert expect
== result
, (expect
, result
)
485 def test_undef(self
) -> None:
486 """Test #undef handling"""
487 expect
= self
.undef_expect
488 result
= self
.cpp
.process_contents(undef_input
)
489 assert expect
== result
, (expect
, result
)
491 def test_macro_function(self
) -> None:
492 """Test using macro functions to express file names"""
493 expect
= self
.macro_function_expect
494 result
= self
.cpp
.process_contents(macro_function_input
)
495 assert expect
== result
, (expect
, result
)
497 def test_token_pasting(self
) -> None:
498 """Test token-pasting to construct file names"""
499 expect
= self
.token_pasting_expect
500 result
= self
.cpp
.process_contents(token_pasting_input
)
501 assert expect
== result
, (expect
, result
)
503 def test_no_space(self
) -> None:
504 """Test no space between #include and the quote"""
505 expect
= self
.no_space_expect
506 result
= self
.cpp
.process_contents(no_space_input
)
507 assert expect
== result
, (expect
, result
)
509 def test_nested_ifs(self
) -> None:
510 expect
= self
.nested_ifs_expect
511 result
= self
.cpp
.process_contents(nested_ifs_input
)
512 assert expect
== result
, (expect
, result
)
514 def test_ifndef(self
) -> None:
515 """Test basic #ifndef processing"""
516 expect
= self
.ifndef_expect
517 result
= self
.cpp
.process_contents(ifndef_input
)
518 assert expect
== result
, (expect
, result
)
520 def test_if_defined_no_space(self
) -> None:
521 """Test #if(defined, i.e.without space but parenthesis"""
522 expect
= self
.if_defined_no_space_expect
523 result
= self
.cpp
.process_contents(if_defined_no_space_input
)
524 assert expect
== result
, (expect
, result
)
526 def test_if_no_space(self
) -> None:
527 """Test #if(, i.e. without space but parenthesis"""
528 expect
= self
.if_no_space_expect
529 result
= self
.cpp
.process_contents(if_no_space_input
)
530 assert expect
== result
, (expect
, result
)
533 class cppAllTestCase(cppTestCase
):
534 def setUp(self
) -> None:
535 self
.cpp
= self
.cpp_class(current
= ".",
536 cpppath
= ['/usr/include'],
537 dict={"SHELL_ESCAPED_H": '\\"file-shell-computed-yes\\"'},
540 class PreProcessorTestCase(cppAllTestCase
):
541 cpp_class
= cpp
.PreProcessor
544 ('include', '"', 'file1-yes'),
545 ('include', '<', 'file2-yes'),
548 substitution_expect
= [
549 ('include', '"', 'file3-yes'),
550 ('include', '<', 'file4-yes'),
551 ('include', '"', 'file5-yes'),
552 ('include', '<', 'file6-yes'),
553 ('include', '"', 'file-shell-computed-yes'),
557 ('include', '"', 'file7-yes'),
558 ('include', '<', 'file8-yes'),
561 if_boolean_expect
= [
562 ('include', '"', 'file9-yes'),
563 ('include', '<', 'file10-yes'),
564 ('include', '"', 'file11-yes'),
565 ('include', '<', 'file12-yes'),
566 ('include', '"', 'file13-yes'),
567 ('include', '<', 'file14-yes'),
570 if_defined_expect
= [
571 ('include', '"', 'file15-yes'),
572 ('include', '<', 'file16-yes'),
573 ('include', '"', 'file17-yes'),
574 ('include', '<', 'file18-yes'),
575 ('include', '<', 'file19-yes'),
578 expression_expect
= [
579 ('include', '"', 'file19-yes'),
580 ('include', '<', 'file20-yes'),
581 ('include', '"', 'file21-yes'),
582 ('include', '<', 'file22-yes'),
583 ('include', '"', 'file23-yes'),
584 ('include', '<', 'file24-yes'),
585 ('include', '"', 'file25-yes'),
586 ('include', '<', 'file26-yes'),
587 ('include', '"', 'file27-yes'),
588 ('include', '<', 'file28-yes'),
589 ('include', '"', 'file29-yes'),
590 ('include', '<', 'file30-yes'),
591 ('include', '<', 'file301-yes'),
595 ('include', '"', 'file31-yes'),
596 ('include', '<', 'file32-yes'),
599 macro_function_expect
= [
600 ('include', '"', 'file33-yes'),
601 ('include', '<', 'file34-yes'),
602 ('include', '"', 'file35-yes'),
603 ('include', '<', 'file36-yes'),
604 ('include', '"', 'file37-yes'),
605 ('include', '<', 'file38-yes'),
606 ('include', '"', 'file39-yes'),
607 ('include', '<', 'file40-yes'),
610 token_pasting_expect
= [
611 ('include', '"', 'file41-yes'),
612 ('include', '<', 'file42-yes'),
616 ('include', '<', 'file43-yes'),
617 ('include', '"', 'file44-yes'),
620 nested_ifs_expect
= [
621 ('include', '"', 'file7-yes'),
625 ('include', '"', 'file45-yes'),
626 ('include', '<', 'file46-yes'),
629 if_defined_no_space_expect
= [
630 ('include', '"', 'file47-yes'),
631 ('include', '<', 'file50-yes'),
632 ('include', '"', 'file53-yes'),
635 if_no_space_expect
= [
636 ('include', '<', 'file55-yes'),
637 ('include', '<', 'file58-yes'),
638 ('include', '"', 'file59-yes'),
639 ('include', '<', 'file62-yes'),
642 class DumbPreProcessorTestCase(cppAllTestCase
):
643 cpp_class
= cpp
.DumbPreProcessor
646 ('include', '"', 'file1-yes'),
647 ('include', '<', 'file2-yes'),
650 substitution_expect
= [
651 ('include', '"', 'file3-yes'),
652 ('include', '<', 'file4-yes'),
653 ('include', '"', 'file5-yes'),
654 ('include', '<', 'file6-yes'),
655 ('include', '"', 'file-shell-computed-yes'),
659 ('include', '"', 'file7-yes'),
660 ('include', '"', 'file7-no'),
661 ('include', '<', 'file8-no'),
662 ('include', '<', 'file8-yes'),
665 if_boolean_expect
= [
666 ('include', '"', 'file9-no'),
667 ('include', '"', 'file9-yes'),
668 ('include', '<', 'file10-yes'),
669 ('include', '<', 'file10-no'),
670 ('include', '"', 'file11-no-1'),
671 ('include', '"', 'file11-no-2'),
672 ('include', '"', 'file11-yes'),
673 ('include', '<', 'file12-no-1'),
674 ('include', '<', 'file12-yes'),
675 ('include', '<', 'file12-no-2'),
676 ('include', '"', 'file13-yes'),
677 ('include', '"', 'file13-no-1'),
678 ('include', '"', 'file13-no-2'),
679 ('include', '<', 'file14-yes'),
680 ('include', '<', 'file14-no-1'),
681 ('include', '<', 'file14-no-2'),
684 if_defined_expect
= [
685 ('include', '"', 'file15-yes'),
686 ('include', '<', 'file16-no'),
687 ('include', '<', 'file16-yes'),
688 ('include', '"', 'file17-yes'),
689 ('include', '<', 'file18-no'),
690 ('include', '<', 'file18-yes'),
691 ('include', '<', 'file19-no'),
692 ('include', '<', 'file19-yes'),
695 expression_expect
= [
696 ('include', '"', 'file19-no'),
697 ('include', '"', 'file19-yes'),
698 ('include', '<', 'file20-no'),
699 ('include', '<', 'file20-yes'),
700 ('include', '"', 'file21-no'),
701 ('include', '"', 'file21-yes'),
702 ('include', '<', 'file22-yes'),
703 ('include', '<', 'file22-no'),
704 ('include', '"', 'file23-no'),
705 ('include', '"', 'file23-yes'),
706 ('include', '<', 'file24-yes'),
707 ('include', '<', 'file24-no'),
708 ('include', '"', 'file25-yes'),
709 ('include', '"', 'file25-no'),
710 ('include', '<', 'file26-yes'),
711 ('include', '<', 'file26-no'),
712 ('include', '"', 'file27-yes'),
713 ('include', '"', 'file27-no'),
714 ('include', '<', 'file28-no'),
715 ('include', '<', 'file28-yes'),
716 ('include', '"', 'file29-no'),
717 ('include', '"', 'file29-yes'),
718 ('include', '<', 'file30-yes'),
719 ('include', '<', 'file30-no'),
720 ('include', '<', 'file301-yes'),
721 ('include', '<', 'file301-no'),
725 ('include', '"', 'file31-yes'),
726 ('include', '"', 'file31-no'),
727 ('include', '<', 'file32-no'),
728 ('include', '<', 'file32-yes'),
731 macro_function_expect
= [
732 ('include', '"', 'file33-yes'),
733 ('include', '<', 'file34-yes'),
734 ('include', '"', 'file35-yes'),
735 ('include', '<', 'file36-yes'),
736 ('include', '"', 'file37-yes'),
737 ('include', '<', 'file38-yes'),
738 ('include', '"', 'file39-yes'),
739 ('include', '<', 'file40-yes'),
742 token_pasting_expect
= [
743 ('include', '"', 'file41-yes'),
744 ('include', '<', 'file42-yes'),
748 ('include', '<', 'file43-yes'),
749 ('include', '"', 'file44-yes'),
752 nested_ifs_expect
= [
753 ('include', '"', 'file7-no'),
754 ('include', '"', 'file8-no'),
755 ('include', '"', 'file9-no'),
756 ('include', '"', 'file7-yes')
760 ('include', '"', 'file45-no'),
761 ('include', '"', 'file45-yes'),
762 ('include', '<', 'file46-yes'),
763 ('include', '<', 'file46-no'),
766 if_defined_no_space_expect
= [
767 ('include', '"', 'file47-yes'),
768 ('include', '<', 'file48-no'),
769 ('include', '<', 'file49-no'),
770 ('include', '<', 'file50-yes'),
771 ('include', '"', 'file51-no'),
772 ('include', '<', 'file52-no'),
773 ('include', '"', 'file53-yes'),
776 if_no_space_expect
= [
777 ('include', '"', 'file54-no'),
778 ('include', '<', 'file55-yes'),
779 ('include', '<', 'file56-no'),
780 ('include', '"', 'file57-no'),
781 ('include', '<', 'file58-yes'),
782 ('include', '"', 'file59-yes'),
783 ('include', '<', 'file60-no'),
784 ('include', '"', 'file61-no'),
785 ('include', '<', 'file62-yes'),
795 def _clean() -> None:
797 if os
.path
.exists(dir):
800 atexit
.register(_clean
)
802 if os
.name
in ('posix', 'nt'):
803 tmpprefix
= 'cppTests.' + str(os
.getpid()) + '.'
805 tmpprefix
= 'cppTests.'
807 class fileTestCase(unittest
.TestCase
):
808 cpp_class
= cpp
.DumbPreProcessor
810 def setUp(self
) -> None:
811 path
= tempfile
.mkdtemp(prefix
=tmpprefix
)
812 _Cleanup
.append(path
)
814 self
.orig_cwd
= os
.getcwd()
817 def tearDown(self
) -> None:
818 os
.chdir(self
.orig_cwd
)
819 shutil
.rmtree(self
.tempdir
)
820 _Cleanup
.remove(self
.tempdir
)
822 def strip_initial_spaces(self
, s
):
823 lines
= s
.split('\n')
824 spaces
= re
.match(' *', lines
[0]).group(0)
825 def strip_spaces(l
, spaces
=spaces
):
826 if l
[:len(spaces
)] == spaces
:
829 return '\n'.join(map(strip_spaces
, lines
))
831 def write(self
, file, contents
) -> None:
832 with
open(file, 'w') as f
:
833 f
.write(self
.strip_initial_spaces(contents
))
835 def test_basic(self
) -> None:
836 """Test basic file inclusion"""
837 self
.write('f1.h', """\
840 self
.write('f2.h', """\
843 self
.write('f3.h', """\
845 p
= cpp
.DumbPreProcessor(current
= os
.curdir
,
846 cpppath
= [os
.curdir
])
848 assert result
== ['f2.h', 'f3.h'], result
850 def test_current_file(self
) -> None:
851 """Test use of the .current_file attribute"""
852 self
.write('f1.h', """\
855 self
.write('f2.h', """\
858 self
.write('f3.h', """\
860 class MyPreProcessor(cpp
.DumbPreProcessor
):
861 def __init__(self
, *args
, **kw
) -> None:
862 super().__init
__(*args
, **kw
)
864 def __call__(self
, file):
865 self
.files
.append(file)
866 return cpp
.DumbPreProcessor
.__call
__(self
, file)
867 def scons_current_file(self
, t
):
868 r
= cpp
.DumbPreProcessor
.scons_current_file(self
, t
)
869 self
.files
.append(self
.current_file
)
871 p
= MyPreProcessor(current
= os
.curdir
, cpppath
= [os
.curdir
])
873 assert result
== ['f2.h', 'f3.h'], result
874 assert p
.files
== ['f1.h', 'f2.h', 'f3.h', 'f2.h', 'f1.h'], p
.files
878 if __name__
== '__main__':
879 suite
= unittest
.TestSuite()
880 tclasses
= [ PreProcessorTestCase
,
881 DumbPreProcessorTestCase
,
884 for tclass
in tclasses
:
885 loader
= unittest
.TestLoader()
886 loader
.testMethodPrefix
= 'test_'
887 names
= loader
.getTestCaseNames(tclass
)
889 names
= sorted(set(names
))
892 suite
.addTests(list(map(tclass
, names
)))
897 # indent-tabs-mode:nil
899 # vim: set expandtab tabstop=4 shiftwidth=4: