1#!/usr/bin/env python
2
3from __future__ import print_function
4import sys, os, re
5
6classes_ignore_list = (
7    'OpenCV(Test)?Case',
8    'OpenCV(Test)?Runner',
9    'CvException',
10)
11
12funcs_ignore_list = (
13    '\w+--HashCode',
14    'Mat--MatLong',
15    '\w+--Equals',
16    'Core--MinMaxLocResult',
17)
18
19class JavaParser:
20    def __init__(self):
21        self.clear()
22
23    def clear(self):
24        self.mdict = {}
25        self.tdict = {}
26        self.mwhere = {}
27        self.twhere = {}
28        self.empty_stubs_cnt = 0
29        self.r1 = re.compile("\s*public\s+(?:static\s+)?(\w+)\(([^)]*)\)") # c-tor
30        self.r2 = re.compile("\s*(?:(?:public|static|final)\s+){1,3}\S+\s+(\w+)\(([^)]*)\)")
31        self.r3 = re.compile('\s*fail\("Not yet implemented"\);') # empty test stub
32
33
34    def dict2set(self, d):
35        s = set()
36        for f in d.keys():
37            if len(d[f]) == 1:
38                s.add(f)
39            else:
40                s |= set(d[f])
41        return s
42
43
44    def get_tests_count(self):
45        return len(self.tdict)
46
47    def get_empty_stubs_count(self):
48        return self.empty_stubs_cnt
49
50    def get_funcs_count(self):
51        return len(self.dict2set(self.mdict)), len(self.mdict)
52
53    def get_not_tested(self):
54        mset = self.dict2set(self.mdict)
55        tset = self.dict2set(self.tdict)
56        nottested = mset - tset
57        out = set()
58
59        for name in nottested:
60            out.add(name + "   " + self.mwhere[name])
61
62        return out
63
64
65    def parse(self, path):
66        if ".svn" in path:
67            return
68        if os.path.isfile(path):
69            if path.endswith("FeatureDetector.java"):
70                for prefix1 in ("", "Grid", "Pyramid", "Dynamic"):
71                    for prefix2 in ("FAST", "STAR", "MSER", "ORB", "SIFT", "SURF", "GFTT", "HARRIS", "SIMPLEBLOB", "DENSE"):
72                        parser.parse_file(path,prefix1+prefix2)
73            elif path.endswith("DescriptorExtractor.java"):
74                for prefix1 in ("", "Opponent"):
75                    for prefix2 in ("BRIEF", "ORB", "SIFT", "SURF"):
76                        parser.parse_file(path,prefix1+prefix2)
77            elif path.endswith("GenericDescriptorMatcher.java"):
78                for prefix in ("OneWay", "Fern"):
79                    parser.parse_file(path,prefix)
80            elif path.endswith("DescriptorMatcher.java"):
81                for prefix in ("BruteForce", "BruteForceHamming", "BruteForceHammingLUT", "BruteForceL1", "FlannBased", "BruteForceSL2"):
82                    parser.parse_file(path,prefix)
83            else:
84                parser.parse_file(path)
85        elif os.path.isdir(path):
86            for x in os.listdir(path):
87                self.parse(path + "/" + x)
88        return
89
90
91    def parse_file(self, fname, prefix = ""):
92        istest = fname.endswith("Test.java")
93        clsname = os.path.basename(fname).replace("Test", "").replace(".java", "")
94        clsname = prefix + clsname[0].upper() + clsname[1:]
95        for cls in classes_ignore_list:
96            if re.match(cls, clsname):
97                return
98        f = open(fname, "rt")
99        linenum = 0
100        for line in f:
101            linenum += 1
102            m1 = self.r1.match(line)
103            m2 = self.r2.match(line)
104            m3 = self.r3.match(line)
105            func = ''
106            args_str = ''
107            if m1:
108                func = m1.group(1)
109                args_str = m1.group(2)
110            elif m2:
111                if "public" not in line:
112                    continue
113                func = m2.group(1)
114                args_str = m2.group(2)
115            elif m3:
116                self.empty_stubs_cnt += 1
117                continue
118            else:
119                #if "public" in line:
120                    #print "UNRECOGNIZED: " + line
121                continue
122            d = (self.mdict, self.tdict)[istest]
123            w = (self.mwhere, self.twhere)[istest]
124            func = re.sub(r"^test", "", func)
125            func = clsname + "--" + func[0].upper() + func[1:]
126            args_str = args_str.replace("[]", "Array").replace("...", "Array ")
127            args_str = re.sub(r"List<(\w+)>", "ListOf\g<1>", args_str)
128            args_str = re.sub(r"List<(\w+)>", "ListOf\g<1>", args_str)
129            args = [a.split()[0] for a in args_str.split(",") if a]
130            func_ex = func + "".join([a[0].upper() + a[1:] for a in args])
131            func_loc = fname + " (line: " + str(linenum)  + ")"
132            skip = False
133            for fi in funcs_ignore_list:
134                if re.match(fi, func_ex):
135                    skip = True
136                    break
137            if skip:
138                continue
139            if func in d:
140                d[func].append(func_ex)
141            else:
142                d[func] = [func_ex]
143            w[func_ex] = func_loc
144            w[func] = func_loc
145
146        f.close()
147        return
148
149
150if __name__ == '__main__':
151    if len(sys.argv) < 2:
152        print("Usage:\n", \
153            os.path.basename(sys.argv[0]), \
154            "<Classes/Tests dir1/file1> [<Classes/Tests dir2/file2> ...]\n", "Not tested methods are loggedto stdout.")
155        exit(0)
156    parser = JavaParser()
157    for x in sys.argv[1:]:
158        parser.parse(x)
159    funcs = parser.get_not_tested()
160    if funcs:
161        print ('{} {}'.format("NOT TESTED methods:\n\t", "\n\t".join(sorted(funcs))))
162    print ("Total methods found: %i (%i)" % parser.get_funcs_count())
163    print ('{} {}'.format("Not tested methods found:", len(funcs)))
164    print ('{} {}'.format("Total tests found:", parser.get_tests_count()))
165    print ('{} {}'.format("Empty test stubs found:", parser.get_empty_stubs_count()))
166