Skip to content
This repository was archived by the owner on Nov 4, 2022. It is now read-only.

Commit 2a64d31

Browse files
author
Albert Allagulov
committed
check compound names
1 parent 7fd88e7 commit 2a64d31

20 files changed

+154
-51
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
2121
# SOFTWARE.
2222

23+
SHELL := /bin/bash
2324
all: install search clone calc summary draw article
2425

2526
install:
@@ -62,9 +63,8 @@ calc:
6263
m="metrics/$${f/clones\/\//}.m"; \
6364
mkdir -p $$(dirname "$${m}"); \
6465
if [ ! -e "$${m}" ]; then \
65-
python3 calc.py "$${f}"; \
66+
python3 computation/calc.py "$${f}" > "$${m}"; \
6667
fi \
67-
# echo '1,1' > "$${m}"; \
6868
done; \
6969
echo "$$(find $${d} -type f | wc -l) Java classes analyzed into $${d}"; \
7070
fi; \

calc/calc_integration_test.py

Lines changed: 0 additions & 41 deletions
This file was deleted.

calc/calc.py renamed to computation/calc.py

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,34 +21,57 @@
2121
# SOFTWARE.
2222

2323
import sys
24+
import re
2425
from javalang import tree, parse
2526

27+
"""Determines the number of branches for a node
28+
according to the Extended Cyclomatic Complexity metric.
29+
Binary operations (&&, ||) and each case statement are taken into account.
30+
31+
:param node: class provided by the parser and targeted to Java 8 spec
32+
:returns: number
33+
"""
2634
def branches(node):
35+
count = 0
2736
if (isinstance(node, tree.BinaryOperation)):
2837
if(node.operator == '&&' or node.operator == '||'):
29-
return 1
30-
31-
if(isinstance(node, (
38+
count = 1
39+
elif(isinstance(node, (
3240
tree.ForStatement,
3341
tree.IfStatement,
3442
tree.WhileStatement,
3543
tree.DoStatement,
3644
tree.TernaryExpression
3745
))):
38-
return 1
46+
count = 1
47+
elif(isinstance(node, tree.SwitchStatementCase)):
48+
count = len(node.case)
49+
elif(isinstance(node, tree.TryStatement)):
50+
count = len(node.catches)
51+
return count
3952

40-
if(isinstance(node, tree.SwitchStatementCase)):
41-
return len(node.case)
53+
"""Check the name for compound inside the methods (i.e. for local variables)
4254
43-
return 0
55+
:param node: class provided by the parser and targeted to Java 8 spec
56+
:returns: boolean
57+
"""
58+
def compound(node):
59+
flag = False
60+
if (isinstance(node, tree.LocalVariableDeclaration)):
61+
name = node.declarators[0].name
62+
flag = len(re.findall(r'(?:[a-z][A-Z]+)|(?:[_])', name)) != 0
63+
return flag
4464

4565

4666
java = sys.argv[1]
4767
with open(java, encoding='utf-8') as f:
4868
try:
4969
cc = 1
70+
names = 0
5071
ast = parse.parse(f.read())
5172
for path, node in ast:
5273
cc += branches(node)
74+
names += int(compound(node))
75+
print(str(cc) + ',' + str(names))
5376
except Exception as e:
5477
sys.exit(str(e) + ': ' + java)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import os
2+
import sys
3+
import unittest
4+
import javalang
5+
from glob import glob
6+
import re
7+
8+
try:
9+
from unittest.mock import patch
10+
except ImportError:
11+
from mock import patch
12+
13+
fileDir = os.path.dirname(os.path.realpath('__file__'))
14+
testargs = ["", os.path.join(fileDir, 'java/cc/SwitchCaseStatement.java')]
15+
with patch.object(sys, 'argv', testargs):
16+
from calc import branches, compound
17+
18+
def cc(tree):
19+
var = next(tree.filter(javalang.tree.VariableDeclarator))[1]
20+
if (var.name != 'cc'):
21+
raise ValueError('file not tested')
22+
return int(var.initializer.value)
23+
24+
def names(ast):
25+
comment = next(ast.filter(javalang.tree.Documented))[1]
26+
return int(re.search(r'[\d]+', comment.documentation).group(0))
27+
28+
for java in glob('java/names/*.java'):
29+
with open(java, encoding='utf-8') as f:
30+
try:
31+
ast = javalang.parse.parse(f.read())
32+
receivedNames = 0
33+
expectedNames = names(ast)
34+
35+
for path, node in ast:
36+
receivedNames += compound(node)
37+
38+
if (receivedNames != expectedNames):
39+
raise Exception('\nTest failed. Expected ' + str(expectedNames) + ', receivedNames ' + str(receivedNames))
40+
except Exception as e:
41+
sys.exit(str(e) + ': ' + java)
42+
43+
for java in glob('java/cc/*.java'):
44+
with open(java, encoding='utf-8') as f:
45+
try:
46+
ast = javalang.parse.parse(f.read())
47+
receivedCC = 1
48+
expectedCC = cc(ast)
49+
50+
for path, node in ast:
51+
receivedCC += branches(node)
52+
53+
if (receivedCC != expectedCC):
54+
raise Exception('\nTest failed. Expected ' + str(expectedCC) + ', receivedCC ' + str(receivedCC))
55+
56+
print('.', end='', flush=True),
57+
except Exception as e:
58+
sys.exit(str(e) + ': ' + java)
59+
60+
print(' OK')
61+

calc/calc_unit_test.py renamed to computation/calc_unit_test.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
fileDir = os.path.dirname(os.path.realpath('__file__'))
1212
testargs = ["", os.path.join(fileDir, 'java/cc/SwitchCaseStatement.java')]
1313
with patch.object(sys, 'argv', testargs):
14-
from calc import branches
14+
from calc import branches, compound
1515

1616
class TestCalc(unittest.TestCase):
1717
def test_for_statement_count(self):
@@ -87,6 +87,35 @@ def test_ternary_operator(self):
8787
node = self.expression(code);
8888
self.assertEqual(branches(node), 1)
8989

90+
def test_catch_statements(self):
91+
code = """
92+
try {
93+
Throwable t = new Exception();
94+
throw t;
95+
} catch (RuntimeException e) {
96+
System.err.println("catch RuntimeException");
97+
} catch (Exception e) {
98+
System.err.println("catch Exception");
99+
} catch (Throwable e) {
100+
System.err.println("catch Throwable");
101+
}
102+
System.err.println("next statement");
103+
"""
104+
node = self.statement(code)
105+
self.assertEqual(branches(node), 3)
106+
107+
def test_compound_with_camel_and_snake_cases(self):
108+
for s in ['camelCase', 'CamelCase', 'camelCASE', 'snake_case', 'snake_CASE', 'SNAKE_CASE', 'SNAKE_case']:
109+
code = "int %s = 0;" % (s)
110+
node = self.parser(code).parse_local_variable_declaration_statement()
111+
self.assertEqual(compound(node), 1)
112+
113+
def test_compound_without_camel_and_snake_cases(self):
114+
for s in ['camelcase, CAMELCASE, Camelcase']:
115+
code = "int %s = 0;" % (s)
116+
node = self.parser(code).parse_local_variable_declaration_statement()
117+
self.assertEqual(compound(node), 0)
118+
90119
def expression(self, code):
91120
return self.parser(code).parse_expression()
92121

File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

0 commit comments

Comments
 (0)