diff --git a/persper/analytics/go.py b/persper/analytics/go.py index 0996e82e9af..284ab5cf6d0 100644 --- a/persper/analytics/go.py +++ b/persper/analytics/go.py @@ -42,7 +42,7 @@ def get_graph(self): r = requests.get(graph_url) graph_data = r.json() graph_data['directed'] = True - graph_data['multigraph'] = True + graph_data['multigraph'] = False return CallCommitGraph(graph_data) def reset_graph(self): diff --git a/test/go_test_repo/D/main.go b/test/go_test_repo/D/main.go new file mode 100644 index 00000000000..7b56028e2e8 --- /dev/null +++ b/test/go_test_repo/D/main.go @@ -0,0 +1,31 @@ +package main +import( + "fmt" + "math" +) +type a func() + +type Vertex struct { + X, Y float64 +} + +func (v Vertex) Abs() float64 { + return math.Sqrt(v.X*v.X + v.Y*v.Y) +} +func (v *Vertex) Absp() float64{ + return math.Sqrt(v.X*v.X + v.Y*v.Y) +} + +func funcA () { + fmt.Println("func A is called!") +} + +func main() { + a := funcA + a() + v := Vertex{3, 4} + fmt.Println(v.Abs()) + p := &Vertex{4,5} + fmt.Println(p.Absp()) +} + diff --git a/test/go_test_repo/cg.dot b/test/go_test_repo/cg.dot index 58861dbba42..f0ae1657596 100644 --- a/test/go_test_repo/cg.dot +++ b/test/go_test_repo/cg.dot @@ -1,3 +1,3 @@ digraph go_test_repo { - A -> B -> C; + A -> B -> C -> D; } \ No newline at end of file diff --git a/test/go_test_repo_1/A/calcproj/src/simplemath/add.go b/test/go_test_repo_1/A/calcproj/src/simplemath/add.go new file mode 100644 index 00000000000..971a9d119b1 --- /dev/null +++ b/test/go_test_repo_1/A/calcproj/src/simplemath/add.go @@ -0,0 +1,5 @@ +package sm + +func Add(a int, b int) int { + return a + b +} \ No newline at end of file diff --git a/test/go_test_repo_1/A/calcproj/src/simplemath/add_test.go b/test/go_test_repo_1/A/calcproj/src/simplemath/add_test.go new file mode 100644 index 00000000000..f488a25ddb0 --- /dev/null +++ b/test/go_test_repo_1/A/calcproj/src/simplemath/add_test.go @@ -0,0 +1,11 @@ +package sm + +import "testing" + +func TestAdd1(t *testing.T) { + r := Add(1, 2) + + if r != 3 { + t.Errorf("Add(1, 2) failed. Got %d, expected 3.", r) + } +} \ No newline at end of file diff --git a/test/go_test_repo_1/A/calcproj/src/simplemath/sqrt.go b/test/go_test_repo_1/A/calcproj/src/simplemath/sqrt.go new file mode 100644 index 00000000000..74402c652d7 --- /dev/null +++ b/test/go_test_repo_1/A/calcproj/src/simplemath/sqrt.go @@ -0,0 +1,8 @@ +package sm + +import "math" + +func Sqrt(i int) int { + v := math.Sqrt(float64(i)) + return int(v) +} \ No newline at end of file diff --git a/test/go_test_repo_1/A/calcproj/src/simplemath/sqrt_test.go b/test/go_test_repo_1/A/calcproj/src/simplemath/sqrt_test.go new file mode 100644 index 00000000000..3e9ea1f4a1c --- /dev/null +++ b/test/go_test_repo_1/A/calcproj/src/simplemath/sqrt_test.go @@ -0,0 +1,11 @@ +package sm + +import "testing" + +func TestSqrt1(t *testing.T) { + v := Sqrt(16) + + if v != 4 { + t.Errorf("Sqrt(16) failed. Got %v, expected 4.", v) + } +} \ No newline at end of file diff --git a/test/go_test_repo_1/B/calcproj/src/calc/calc.go b/test/go_test_repo_1/B/calcproj/src/calc/calc.go new file mode 100644 index 00000000000..d5fb92507e2 --- /dev/null +++ b/test/go_test_repo_1/B/calcproj/src/calc/calc.go @@ -0,0 +1,53 @@ +package main + +import ("os" + "fmt" + "strconv" + "gitlab.com/meri.co/test/calcproj/src/simplemath") + +var Usage = func() { + fmt.Println("USAGE: calc command [arguments] ...") + fmt.Println("\nThe commands are:\n\tadd\tAddition of two values.\n\tsqrt\tSquare root of a non-negative value.") +} + +func main() { + args := os.Args + if args == nil || len(args) < 2 { + Usage() + return + } + + switch args[1] { + case "add": + if len(args) != 4 { + fmt.Println("USAGE: calc add ") + return + } + + v1, err1 := strconv.Atoi(args[2]) + v2, err2 := strconv.Atoi(args[3]) + if err1 != nil || err2 != nil { + fmt.Println("USAGE: calc add ") + return + } + + ret := sm.Add(v1, v2) + fmt.Println("Result: ", ret) + case "sqrt": + if len(args) != 3 { + fmt.Println("USAGE: calc sqrt ") + return + } + + v, err := strconv.Atoi(args[2]) + if err != nil { + fmt.Println("USAGE: calc sqrt ") + return + } + + ret := sm.Sqrt(v) + fmt.Println("Result: ", ret) + default: + Usage() + } +} \ No newline at end of file diff --git a/test/go_test_repo_1/B/calcproj/src/simplemath/add.go b/test/go_test_repo_1/B/calcproj/src/simplemath/add.go new file mode 100644 index 00000000000..971a9d119b1 --- /dev/null +++ b/test/go_test_repo_1/B/calcproj/src/simplemath/add.go @@ -0,0 +1,5 @@ +package sm + +func Add(a int, b int) int { + return a + b +} \ No newline at end of file diff --git a/test/go_test_repo_1/B/calcproj/src/simplemath/add_test.go b/test/go_test_repo_1/B/calcproj/src/simplemath/add_test.go new file mode 100644 index 00000000000..f488a25ddb0 --- /dev/null +++ b/test/go_test_repo_1/B/calcproj/src/simplemath/add_test.go @@ -0,0 +1,11 @@ +package sm + +import "testing" + +func TestAdd1(t *testing.T) { + r := Add(1, 2) + + if r != 3 { + t.Errorf("Add(1, 2) failed. Got %d, expected 3.", r) + } +} \ No newline at end of file diff --git a/test/go_test_repo_1/B/calcproj/src/simplemath/sqrt.go b/test/go_test_repo_1/B/calcproj/src/simplemath/sqrt.go new file mode 100644 index 00000000000..74402c652d7 --- /dev/null +++ b/test/go_test_repo_1/B/calcproj/src/simplemath/sqrt.go @@ -0,0 +1,8 @@ +package sm + +import "math" + +func Sqrt(i int) int { + v := math.Sqrt(float64(i)) + return int(v) +} \ No newline at end of file diff --git a/test/go_test_repo_1/B/calcproj/src/simplemath/sqrt_test.go b/test/go_test_repo_1/B/calcproj/src/simplemath/sqrt_test.go new file mode 100644 index 00000000000..3e9ea1f4a1c --- /dev/null +++ b/test/go_test_repo_1/B/calcproj/src/simplemath/sqrt_test.go @@ -0,0 +1,11 @@ +package sm + +import "testing" + +func TestSqrt1(t *testing.T) { + v := Sqrt(16) + + if v != 4 { + t.Errorf("Sqrt(16) failed. Got %v, expected 4.", v) + } +} \ No newline at end of file diff --git a/test/go_test_repo_1/cg.dot b/test/go_test_repo_1/cg.dot new file mode 100644 index 00000000000..387bee3f469 --- /dev/null +++ b/test/go_test_repo_1/cg.dot @@ -0,0 +1,3 @@ +digraph go_test_repo { + A -> B ; +} \ No newline at end of file diff --git a/test/go_test_repo_2/A/main.go b/test/go_test_repo_2/A/main.go new file mode 100644 index 00000000000..5398e6dcb6f --- /dev/null +++ b/test/go_test_repo_2/A/main.go @@ -0,0 +1,30 @@ +package main +import( + "fmt" +) +//This test code is used for test interface function call +type animal interface { + printInfo() +} + +type cat int +type dog int + +func (c cat) printInfo(){ + fmt.Println("a cat") +} + +func (d dog) printInfo(){ + fmt.Println("a dog") +} + +func main() { + var a animal + var c cat + a=c + a.printInfo() + //other type + var d dog + a=d + a.printInfo() +} \ No newline at end of file diff --git a/test/go_test_repo_2/B/main.go b/test/go_test_repo_2/B/main.go new file mode 100644 index 00000000000..2a813999d0c --- /dev/null +++ b/test/go_test_repo_2/B/main.go @@ -0,0 +1,28 @@ +package main +import( + "fmt" +) +//This test code is used for test interface function call +type animal interface { + printInfo() +} + +type cat int +type dog int +func (c cat) printInfo(){ + fmt.Println("a cat") +} + +func (c dog) printInfo(){ + fmt.Println("a dog") +} +func invoke(a animal){ + a.printInfo() +} +func main() { + var c cat + var d dog + //as value convert + invoke(c) + invoke(d) +} diff --git a/test/go_test_repo_2/cg.dot b/test/go_test_repo_2/cg.dot new file mode 100644 index 00000000000..387bee3f469 --- /dev/null +++ b/test/go_test_repo_2/cg.dot @@ -0,0 +1,3 @@ +digraph go_test_repo { + A -> B ; +} \ No newline at end of file diff --git a/test/test_analytics/test_analyzer.py b/test/test_analytics/test_analyzer.py index be652bdb5a5..a5921bd81b7 100644 --- a/test/test_analytics/test_analyzer.py +++ b/test/test_analytics/test_analyzer.py @@ -1,6 +1,7 @@ import os import pytest import subprocess +import shutil from persper.analytics.c import CGraphServer from persper.analytics.analyzer import Analyzer from persper.analytics.graph_server import C_FILENAME_REGEXES @@ -13,9 +14,13 @@ def az(): repo_path = os.path.join(root_path, 'repos/test_feature_branch') script_path = os.path.join(root_path, 'tools/repo_creater/create_repo.py') test_src_path = os.path.join(root_path, 'test/test_feature_branch') - if not os.path.isdir(repo_path): - cmd = '{} {}'.format(script_path, test_src_path) - subprocess.call(cmd, shell=True) + + # Always use latest source to create test repo + if os.path.exists(repo_path): + shutil.rmtree(repo_path) + + cmd = '{} {}'.format(script_path, test_src_path) + subprocess.call(cmd, shell=True) return Analyzer(repo_path, CGraphServer(C_FILENAME_REGEXES)) diff --git a/test/test_analytics/test_analyzer_go.py b/test/test_analytics/test_analyzer_go.py index d2a77e130fa..01e73e6fc19 100644 --- a/test/test_analytics/test_analyzer_go.py +++ b/test/test_analytics/test_analyzer_go.py @@ -1,6 +1,7 @@ import os import time import pytest +import shutil import subprocess from persper.analytics.graph_server import GO_FILENAME_REGEXES from persper.analytics.go import GoGraphServer @@ -25,9 +26,12 @@ def az(): test_src_path = os.path.join(root_path, 'test/go_test_repo') server_addr = 'http://localhost:%d' % server_port - if not os.path.isdir(repo_path): - cmd = '{} {}'.format(script_path, test_src_path) - subprocess.call(cmd, shell=True) + # Always use latest source to create test repo + if os.path.exists(repo_path): + shutil.rmtree(repo_path) + + cmd = '{} {}'.format(script_path, test_src_path) + subprocess.call(cmd, shell=True) return Analyzer(repo_path, GoGraphServer(server_addr, GO_FILENAME_REGEXES)) @@ -38,6 +42,10 @@ def test_analzyer_go(az): ccgraph = az.get_graph() history_truth = { + 'D': {'Abs': 6, + 'funcA': 0, + 'main': 8, + "Absp": 3}, 'C': {'Abs': 5, 'funcA': 0, 'funcB': 1, @@ -78,5 +86,11 @@ def test_analzyer_go(az): ('funcB', 'funcA') ]) - all_edges = edges_added_by_A.union(edges_added_by_B).union(edges_added_by_C) + edges_added_by_D = set([ + ("Absp", "Sqrt"), + ("main", "Absp") + ]) + + print(set(az._graph_server.get_graph().edges())) + all_edges = edges_added_by_A.union(edges_added_by_B).union(edges_added_by_C).union(edges_added_by_D) assert(set(az._graph_server.get_graph().edges()) == all_edges) diff --git a/test/test_analytics/test_analyzer_go_1.py b/test/test_analytics/test_analyzer_go_1.py new file mode 100644 index 00000000000..ed9e0d5a840 --- /dev/null +++ b/test/test_analytics/test_analyzer_go_1.py @@ -0,0 +1,78 @@ +import os +import time +import pytest +import shutil +import subprocess +from persper.analytics.graph_server import GO_FILENAME_REGEXES +from persper.analytics.go import GoGraphServer +from persper.analytics.analyzer import Analyzer +from persper.util.path import root_path + +# TODO: Use a port other than the default 8080 in case of collision +server_port = 8080 + + +@pytest.fixture(scope='module') +def az(): + """ Build the test repo if not already exists + + Args: + repo_path - A string, path to the to-be-built test repo + script_path - A string, path to the repo creator script + test_src_path - A string, path to the dir to be passed to repo creator + """ + repo_path = os.path.join(root_path, 'repos/go_test_repo_1') + script_path = os.path.join(root_path, 'tools/repo_creater/create_repo.py') + test_src_path = os.path.join(root_path, 'test/go_test_repo_1') + server_addr = 'http://localhost:%d' % server_port + + # Always use latest source to create test repo + if os.path.exists(repo_path): + shutil.rmtree(repo_path) + + cmd = '{} {}'.format(script_path, test_src_path) + subprocess.call(cmd, shell=True) + + return Analyzer(repo_path, GoGraphServer(server_addr, GO_FILENAME_REGEXES)) + + +def test_analzyer_go(az): + az._graph_server.reset_graph() + az.analyze() + ccgraph = az.get_graph() + + history_truth = { + 'B': {'Abs': 3, + 'funcA': 0, + 'funcB': 3, + 'main': 5}, + 'A': {'Abs': 3, + 'funcA': 3, + 'main': 6} + } + + commits = ccgraph.commits() + for func, data in ccgraph.nodes(data=True): + history = data['history'] + for cindex, csize in history.items(): + commit_message = commits[int(cindex)]['message'] + assert(csize == history_truth[commit_message.strip()][func]) + + edges_added_by_A = set([ + ('Abs', 'Sqrt'), + ('funcA', 'Println'), + ('main', 'a'), + ('main', 'Println'), + ('main', 'Abs'), + ]) + + edges_added_by_B = set([ + ('Abs', 'funcA'), + ('funcB', 'Println'), + ('main', 'b'), + ('main', 'c'), + ]) + + + all_edges = edges_added_by_A.union(edges_added_by_B) + assert(set(az._graph_server.get_graph().edges()) == all_edges) diff --git a/test/test_analytics/test_analyzer_go_2.py b/test/test_analytics/test_analyzer_go_2.py new file mode 100644 index 00000000000..440fe37cca4 --- /dev/null +++ b/test/test_analytics/test_analyzer_go_2.py @@ -0,0 +1,75 @@ +import os +import time +import pytest +import shutil +import subprocess +from persper.analytics.graph_server import GO_FILENAME_REGEXES +from persper.analytics.go import GoGraphServer +from persper.analytics.analyzer import Analyzer +from persper.util.path import root_path + +# TODO: Use a port other than the default 8080 in case of collision +server_port = 8080 + + +@pytest.fixture(scope='module') +def az(): + """ Build the test repo if not already exists + + Args: + repo_path - A string, path to the to-be-built test repo + script_path - A string, path to the repo creator script + test_src_path - A string, path to the dir to be passed to repo creator + """ + repo_path = os.path.join(root_path, 'repos/go_test_repo_2') + script_path = os.path.join(root_path, 'tools/repo_creater/create_repo.py') + test_src_path = os.path.join(root_path, 'test/go_test_repo_2') + server_addr = 'http://localhost:%d' % server_port + + # Always use latest source to create test repo + if os.path.exists(repo_path): + shutil.rmtree(repo_path) + + cmd = '{} {}'.format(script_path, test_src_path) + subprocess.call(cmd, shell=True) + + return Analyzer(repo_path, GoGraphServer(server_addr, GO_FILENAME_REGEXES)) + + +def test_analzyer_go(az): + az._graph_server.reset_graph() + az.analyze() + ccgraph = az.get_graph() + + history_truth = { + 'A': {'printInfo': 0, + 'main': 10}, + 'B': {'printInfo': 1, + 'main': 8, + "invoke":3} + } + + commits = ccgraph.commits() + for func, data in ccgraph.nodes(data=True): + history = data['history'] + for cindex, csize in history.items(): + commit_message = commits[int(cindex)]['message'] + assert(csize == history_truth[commit_message.strip()][func]) + + edges_added_by_A = set([ + ('main', 'printInfo'), + ('printInfo', 'Println'), + ]) + + edges_added_by_B = set([ + ('invoke', 'printInfo'), + ('main', 'invoke'), + ]) + + edges_added_by_C = set([ + ('Abs', 'a'), + ('funcB', 'funcA') + ]) + + all_edges = edges_added_by_A.union(edges_added_by_B) + assert(set(az._graph_server.get_graph().edges()) == all_edges) diff --git a/test/test_analytics/test_analyzer_js.py b/test/test_analytics/test_analyzer_js.py index e3c50b47176..bb8592d5673 100644 --- a/test/test_analytics/test_analyzer_js.py +++ b/test/test_analytics/test_analyzer_js.py @@ -1,6 +1,7 @@ import os import time import pytest +import shutil import subprocess from persper.analytics.graph_server import JS_FILENAME_REGEXES from persper.analytics.graph_server_http import GraphServerHttp @@ -27,9 +28,12 @@ def az(): test_src_path = os.path.join(root_path, 'test/js_test_repo') server_addr = 'http://localhost:%d' % server_port - if not os.path.isdir(repo_path): - cmd = '{} {}'.format(script_path, test_src_path) - subprocess.call(cmd, shell=True) + # Always use latest source to create test repo + if os.path.exists(repo_path): + shutil.rmtree(repo_path) + + cmd = '{} {}'.format(script_path, test_src_path) + subprocess.call(cmd, shell=True) return Analyzer(repo_path, GraphServerHttp(server_addr, JS_FILENAME_REGEXES)) diff --git a/tools/repo_creater/create_repo.py b/tools/repo_creater/create_repo.py index 4547a2f60c2..4dcd142e10d 100755 --- a/tools/repo_creater/create_repo.py +++ b/tools/repo_creater/create_repo.py @@ -18,16 +18,29 @@ def make_new_dir(dir_path): os.makedirs(dir_path) -def delete_files_under_dir(dir_path): - for e in os.scandir(dir_path): - if e.is_file(): - os.remove(e.path) +def remove_all_except_git(dir_path): + for item in os.listdir(dir_path): + # Do not delete the .git directory + if item == ".git": + continue + path = os.path.join(dir_path, item) + if os.path.isdir(path): + shutil.rmtree(path) + else: + os.remove(path) -def copy_files(src_dir_path, dest_dir_path): - for e in os.scandir(src_dir_path): - if e.is_file(): - shutil.copyfile(e.path, os.path.join(dest_dir_path, e.name)) +def copy_tree(src, dst, symlinks=False, ignore=None): + """ + Copy all files/dirs under src to dst, a wrapper around shutil.copytree + """ + for item in os.listdir(src): + s = os.path.join(src, item) + d = os.path.join(dst, item) + if os.path.isdir(s): + shutil.copytree(s, d, symlinks, ignore) + else: + shutil.copy2(s, d) def find_first_commit(graph): @@ -63,8 +76,8 @@ def simple_commit(repo, commit_dir, repo_path, commit_msg): :param commit_msg: :return: the SHA of newly created commit """ - delete_files_under_dir(repo_path) - copy_files(commit_dir, repo_path) + remove_all_except_git(repo_path) + copy_tree(commit_dir, repo_path) repo.git.add("*") repo.git.commit("-m {}".format(commit_msg)) sha = repo.git.rev_parse("HEAD")