5 # Count statistics about SCons test and source files. This must be run
6 # against a fully-populated tree (for example, one that's been freshly
9 # A test file is anything under the src/ directory that begins with
10 # 'test_' or ends in 'Tests.py', or anything under the test/ directory
11 # that ends in '.py'. Note that runtest.py script does *not*, by default,
12 # consider the files that begin with 'test_' to be tests, because they're
13 # tests of SCons packaging and installation, not functional tests of
16 # A source file is anything under the src/engine/ or src/script/
17 # directories that ends in '.py' but does NOT begin with 'test_'
18 # or end in 'Tests.py'.
20 # We report the number of tests and sources, the total number of lines
21 # in each category, the number of non-blank lines, and the number of
22 # non-comment lines. The last figure (non-comment) lines is the most
23 # interesting one for most purposes.
24 __revision__
= "__FILE__ __REVISION__ __DATE__ __DEVELOPER__"
28 fmt
= "%-16s %5s %7s %9s %11s %11s"
31 def __init__(self
, name
, files
=None, pred
=None):
39 def __call__(self
, fname
):
40 return self
.pred(fname
)
42 return len(self
.files
)
43 def collect(self
, directory
):
44 for dirpath
, dirnames
, filenames
in os
.walk(directory
):
45 try: dirnames
.remove('.svn')
46 except ValueError: pass
47 self
.files
.extend([ os
.path
.join(dirpath
, f
)
48 for f
in filenames
if self
.pred(f
) ])
52 except AttributeError:
53 self
._lines
= lines
= []
54 for file in self
.files
:
55 file_lines
= open(file).readlines()
56 lines
.extend([s
.lstrip() for s
in file_lines
])
59 return [s
for s
in self
.lines() if s
!= '']
60 def non_comment(self
):
61 return [s
for s
in self
.lines() if s
== '' or s
[0] != '#']
62 def non_blank_non_comment(self
):
63 return [s
for s
in self
.lines() if s
!= '' and s
[0] != '#']
65 return (self
._name
+ ':',
68 len(self
.non_blank()),
69 len(self
.non_comment()),
70 len(self
.non_blank_non_comment()))
73 return x
[-8:] == 'Tests.py'
75 return x
[:5] == 'test_'
77 return x
[-3:] == '.py'
79 return is_python(x
) and not is_Tests_py(x
) and not is_test_(x
)
81 src_Tests_py_tests
= Collection('src/ *Tests.py', pred
=is_Tests_py
)
82 src_test_tests
= Collection('src/ test_*.py', pred
=is_test_
)
83 test_tests
= Collection('test/ tests', pred
=is_python
)
84 sources
= Collection('sources', pred
=is_source
)
86 src_Tests_py_tests
.collect('src')
87 src_test_tests
.collect('src')
88 test_tests
.collect('test')
89 sources
.collect('src/engine')
90 sources
.collect('src/script')
92 src_tests
= Collection('src/ tests', src_Tests_py_tests
.files
93 + src_test_tests
.files
)
94 all_tests
= Collection('all tests', src_tests
.files
+ test_tests
.files
)
96 def ratio(over
, under
):
97 return "%.2f" % (float(len(over
)) / float(len(under
)))
99 print(fmt
% ('', '', '', '', '', 'non-blank'))
100 print(fmt
% ('', 'files', 'lines', 'non-blank', 'non-comment', 'non-comment'))
102 print(fmt
% src_Tests_py_tests
.printables())
103 print(fmt
% src_test_tests
.printables())
105 print(fmt
% src_tests
.printables())
106 print(fmt
% test_tests
.printables())
108 print(fmt
% all_tests
.printables())
109 print(fmt
% sources
.printables())
111 print(fmt
% ('ratio:',
112 ratio(all_tests
, sources
),
113 ratio(all_tests
.lines(), sources
.lines()),
114 ratio(all_tests
.non_blank(), sources
.non_blank()),
115 ratio(all_tests
.non_comment(), sources
.non_comment()),
116 ratio(all_tests
.non_blank_non_comment(),
117 sources
.non_blank_non_comment())