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