Skip to content

Commit

Permalink
Golang support (#12)
Browse files Browse the repository at this point in the history
* Add go repo to test

* add go-test-repo

* Add go graph test analyzer

* Fix go_test_repo

* Add directed and multigraph flags to graph json data

* Add GO_FILEANME_REGEXES

* Fix various bugs in test_analyzer_go.py

* Undo changes to CallCommitGraph
  • Loading branch information
UltimateBeaver authored Feb 13, 2019
1 parent c9e5116 commit 074c49e
Show file tree
Hide file tree
Showing 7 changed files with 240 additions and 0 deletions.
59 changes: 59 additions & 0 deletions persper/analytics/go.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
from networkx.readwrite import json_graph
from persper.analytics.graph_server import GraphServer
from persper.analytics.call_commit_graph import CallCommitGraph
import re
import requests
import urllib.parse


class GoGraphServer(GraphServer):
def __init__(self, server_addr, filename_regex_strs):
self.server_addr = server_addr
self.filename_regexes = [re.compile(regex_str) for regex_str in filename_regex_strs]
self.config_param = dict()

def register_commit(self, hexsha, author_name, author_email, commit_message):
# TODO: use 'message' or 'commit_message', but not both
payload = {'hexsha': hexsha,
'author_name': author_name,
'author_email': author_email,
'message': commit_message}
register_url = urllib.parse.urljoin(self.server_addr, '/register_commit')
r = requests.post(register_url, json=payload).json()
if r != '0':
raise ValueError()

def update_graph(self, old_filename, old_src,
new_filename, new_src, patch):
payload = {'oldFname': old_filename,
'oldSrc': old_src,
'newFname': new_filename,
'newSrc': new_src,
'patch': patch.decode('utf-8', 'replace'),
'config': self.config_param}

update_url = urllib.parse.urljoin(self.server_addr, '/update')
r = requests.post(update_url, json=payload).json()
if r != '0':
raise ValueError()

def get_graph(self):
graph_url = self.server_addr + '/callgraph'
r = requests.get(graph_url)
graph_data = r.json()
graph_data['directed'] = True
graph_data['multigraph'] = True
return CallCommitGraph(graph_data)

def reset_graph(self):
reset_url = urllib.parse.urljoin(self.server_addr, '/reset')
requests.post(reset_url)

def filter_file(self, filename):
for regex in self.filename_regexes:
if not regex.match(filename):
return False
return True

def config(self, param):
self.config_param = param
3 changes: 3 additions & 0 deletions persper/analytics/graph_server.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@
r'.+\.(c|cc|cxx|cpp|CPP|c\+\+|C|hh|hpp|Hpp|h\+\+|H)$'
}

GO_FILENAME_REGEXES = [
r'.+\.go$'
]

class GraphServer(ABC):

Expand Down
26 changes: 26 additions & 0 deletions test/go_test_repo/A/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
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 funcA () {
fmt.Println("func A is called!")
}

func main() {
a := funcA
a()
v := Vertex{3, 4}
fmt.Println(v.Abs())
}

35 changes: 35 additions & 0 deletions test/go_test_repo/B/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package main
import(
"fmt"
"math"
)
type a func()
type b func()
type c func()
type Vertex struct {
X, Y float64
}

func (v Vertex) Abs() float64 {
funcA()
return math.Sqrt(v.X*v.X + v.Y*v.Y)


}

func funcA () {
fmt.Println("func A is called!")
}
func funcB () {
fmt.Println("func B is called!")
}
func main() {
a := funcA
b := funcB
c := a
b()
c()
v := Vertex{3, 4}
fmt.Println(v.Abs())
}

32 changes: 32 additions & 0 deletions test/go_test_repo/C/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package main
import(
"fmt"
"math"
)
type a func()
type b func()
type c func()
type Vertex struct {
X, Y float64
}
func (v Vertex) Abs() float64 {
a:=funcA()
a()
return math.Sqrt(v.X*v.X + v.Y*v.Y)
}
func funcA () {
fmt.Println("func A is called!")
}
func funcB () {
funcA()
fmt.Println("func B is called!")
}
func main() {
a := funcA
b := funcB
c := a
b()
c()
v := Vertex{3, 4}
fmt.Println(v.Abs())
}
3 changes: 3 additions & 0 deletions test/go_test_repo/cg.dot
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
digraph go_test_repo {
A -> B -> C;
}
82 changes: 82 additions & 0 deletions test/test_analytics/test_analyzer_go.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import os
import time
import pytest
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')
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')
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)

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 = {
'C': {'Abs': 5,
'funcA': 0,
'funcB': 1,
'main': 0},
'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'),
])

edges_added_by_C = set([
('Abs', 'a'),
('funcB', 'funcA')
])

all_edges = edges_added_by_A.union(edges_added_by_B).union(edges_added_by_C)
assert(set(az._graph_server.get_graph().edges()) == all_edges)

0 comments on commit 074c49e

Please sign in to comment.