def store(self, db_helper: DatabaseHelper, project: Project, commit: Commit, coverage: List[Tuple[TestMethod, List[LineCoverage]]]): with db_helper.create_session() as session: project, _ = session.add(project) commit.project = project commit, _ = session.add(commit) for test, lines in coverage: test.project = project test, _ = session.add(test) for line in lines: method_version: ProdMethodVersion = session.query(ProdMethodVersion)\ .join(ProdMethod, (ProdMethod.id==ProdMethodVersion.method_id) & (ProdMethodVersion.line_start <= line.line_number) & (ProdMethodVersion.line_end >= line.line_number) )\ .filter(ProdMethod.file_path.contains(line.full_name))\ .filter(ProdMethodVersion.commit_id==commit.id)\ .first() line.test_id = test.id line.commit_id = commit.id if method_version is None: continue line.method_version_id = method_version.id session.add(line)
class TestCoverageAndMethodParser(unittest.TestCase): def setUp(self): self.resource_dir = f'.{os.path.sep}tests{os.path.sep}resources{os.path.sep}' self.db_helper = DatabaseHelper(':memory:') def tearDown(self): pass def load_coverage(self, project_name, commit_sha) -> List[Dict]: coverage_matrix_file = f'{self.resource_dir}{project_name}{os.path.sep}{commit_sha}-cov-matrix.json' with open(coverage_matrix_file) as coverage: return json.load(coverage) def load_methods(self, project_name, commit_sha) -> List[Dict]: methods_file = f'{self.resource_dir}{project_name}{os.path.sep}methods-{commit_sha}.json' with open(methods_file) as methods: return json.load(methods) def test_parsing_and_storing_coverage_data(self): # Given: some coverage information project_name = 'mid_example' commit_sha = 'df1bc2481a05acc3944cc1c3f637856d54cd8ba8' project = Project(project_name=project_name) commit = Commit(sha=commit_sha) # When method_parser: MethodParser = MethodParser() methods_dict: List[Dict] = self.load_methods(project_name, commit_sha) methods = method_parser\ .set_commit(commit)\ .parse(methods_dict) method_parser.store(self.db_helper, project, commit, methods) tacoco_parser: TacocoParser = TacocoParser() coverage_dict: Dict = self.load_coverage(project_name, commit_sha) coverage = tacoco_parser\ .parse(coverage_dict) tacoco_parser.store(self.db_helper, project, commit, coverage) assert len(self.db_helper.query(LineCoverage).all()) != 0
def create_app(data_base_path, echo=False): app = Flask(__name__) app.config['JSON_SORT_KEYS'] = False CORS(app) db_helper: DatabaseHelper = DatabaseHelper(data_base_path, echo) @app.route('/', methods=['GET']) @timer def hello_world(): return "Hello World!", 200 @app.route('/projects', methods=['GET']) @timer def list_projects(): with db_helper.create_session() as session: projects = ProjectQuery(session).get_projects() if len(projects) == 0: return {"Error": "No projects found..."}, 404 return {"projects": list(map(row2dict, projects))}, 200 @app.route('/commits/<project_name>', methods=['GET']) @timer def list_commits_of_project(project_name): with db_helper.create_session() as session: project: Project = ProjectQuery(session).get_project(project_name) if project is None: return {"Error": "Project not found..."}, 404 commits = CommitQuery(session).get_commits(project) if len(commits) == 0: return {"Error": f"Project '{project_name}' was not found..."}, 404 return { "project": project_name, "commits": list(map(row2dict, commits)) }, 200 @app.route('/coverage/<project_name>/<commit_sha>', methods=['GET']) @timer def coverage(project_name, commit_sha): # TODO add error handling (commit not found, project not found) filters = [] # TODO Add test/method filter parameter with db_helper.create_session() as session: project: Project = ProjectQuery(session).get_project( project_name=project_name) if project is None: return {"Error": "Project not found..."}, 404 commit: Commit = CommitQuery(session).get_commit( project, commit_sha) logger.debug(f"\n\n{project}/{commit}\n\n") if commit is None: return {"Error": "Commit not found..."}, 404 coverage_query = MethodCoverageQuery(session)\ .set_commit(commit)\ .set_project(project) for filter in filters: coverage_query.add_filter(filter) edges = coverage_query.get_coverage() methods = coverage_query.get_methods() tests = coverage_query.get_tests() # Format coverage data coverage = coverage_format(methods, tests, edges) # Filter and sort data using the given parametrs. sort_function = list() if (f := sort_selector("name")) is not None: sort_function.append(f) # filter and sort the data coverage = ProcessDataBuilder() \ .add_sorters(sort_function) \ .add_metrics(cluster) \ .process_data(coverage) return { "project": project_name, "commit_sha": commit_sha, "coverage": coverage }, 200
def start(DB_PATH, project_url, output_path, tacoco_path, history_slider_path, arguments): db_handler = DatabaseHelper(DB_PATH) with AnalysisRepo(project_url) as repo: # TODO Add in error handling for existing commits/projects logger.info("Project: %s", repo.get_project_name()) # Add project project = Project(project_name=repo.get_project_name()) # Analysis tools tacoco_runner = TacocoRunner(repo, output_path, tacoco_path) parser_runner = MethodParserRunner(repo, output_path, history_slider_path) # Get all commits we want to run the analysis on. commits: List[Commit] = [] if arguments.current: commits = [repo.get_current_commit()] elif arguments.tags is not None: commits = repo.iterate_tagged_commits(arguments.tags) elif arguments.commits is not None: commits = repo.iterate_commits(arguments.commits) else: print("The run was misconfigured...") exit(0) for commit in commits: logger.info("[INFO] Analyze commit: %s", commit.sha) # Run analysis and return paths to output files success, method_file_path, tacoco_file_path = _analysis( repo, tacoco_runner, parser_runner, output_path) if not success: logger.error('Analysis for %s failed...', commit.sha) continue with open(method_file_path) as method_file: method_parser = MethodParser()\ .set_commit(commit) methods = method_parser\ .parse(json.load(method_file)) if len(methods) != 0: method_parser.store(db_helper=db_handler, project=project, commit=commit, methods=methods) else: logger.error("No methods were parsed...") with open(tacoco_file_path) as tacoco_file: tacoco_parser = TacocoParser() coverage = tacoco_parser\ .parse(json.load(tacoco_file)) if len(coverage) != 0: tacoco_parser.store(db_helper=db_handler, project=project, commit=commit, coverage=coverage) else: logger.error("No coverage was collected.")
def setUp(self): self.resource_dir = f'.{os.path.sep}tests{os.path.sep}resources{os.path.sep}' self.db_helper = DatabaseHelper(':memory:')
class TestQueryCoverageData(unittest.TestCase): def setUp(self): self.db_helper = DatabaseHelper(':memory:') self.resource_dir = f'.{os.path.sep}tests{os.path.sep}resources{os.path.sep}' def tearDown(self): pass def __init_database(self, project, commit): method_parser : MethodParser = MethodParser() methods_dict: List[Dict] = self.__load_methods(project.project_name, commit.sha) methods = method_parser\ .set_commit(commit)\ .parse(methods_dict) method_parser.store(self.db_helper, project, commit, methods) tacoco_parser : TacocoParser = TacocoParser() coverage_dict: Dict = self.__load_coverage(project.project_name, commit.sha) coverage = tacoco_parser\ .parse(coverage_dict) tacoco_parser.store(self.db_helper, project, commit, coverage) def __load_coverage(self, project_name, commit_sha) -> List[Dict]: coverage_matrix_file = f'{self.resource_dir}{project_name}{os.path.sep}{commit_sha}-cov-matrix.json' with open(coverage_matrix_file) as coverage: return json.load(coverage) def __load_methods(self, project_name, commit_sha) -> List[Dict]: methods_file = f'{self.resource_dir}{project_name}{os.path.sep}methods-{commit_sha}.json' with open(methods_file) as methods: return json.load(methods) def test_querying_coverage_primitive_hamcrest(self): # Given: an initialized database project_name = 'primitive-hamcrest' commit_sha = '250f63fe6e70ca6c44ee696c1937b5ccb14f2e6e' project = Project(project_name=project_name) commit = Commit(sha=commit_sha) self.__init_database(project, commit) # When: we query a specific method with self.db_helper.create_session() as session: query = MethodCoverageQuery(session)\ .set_commit(commit)\ .set_project(project) methods = query.get_methods() tests = query.get_tests() edges = query.get_coverage() coverage = coverage_format(methods, tests, edges) coverage = cluster(coverage) assert coverage is not None assert json.dumps(coverage) is not None
class TestMethodParser(unittest.TestCase): def setUp(self): self.resource_dir = f'.{os.path.sep}tests{os.path.sep}resources{os.path.sep}' self.db_helper = DatabaseHelper(':memory:') def tearDown(self): pass def load_methods(self, project_name, commit_sha) -> List[Dict]: methods_file = f'{self.resource_dir}{project_name}{os.path.sep}methods-{commit_sha}.json' with open(methods_file) as methods: return json.load(methods) def test_parsing_methods(self): # Given: some coverage information project_name = 'mid_example' commit_sha = 'df1bc2481a05acc3944cc1c3f637856d54cd8ba8' commit = Commit(sha=commit_sha) methods: List[Dict] = self.load_methods(project_name, commit_sha) # When parsing the coverage file parser: MethodParser = MethodParser().set_commit(commit) # Then: It contain 1 methods (The test methods are filtered out.) results = parser.parse(methods) assert len(results) == 1 def test_adding_single_project_with_two_commits_to_database(self): # Given: some coverage information # Project 1 name = 'mid_example' url = 'https://github.com/jajones/mid_example' commit_sha = 'df1bc2481a05acc3944cc1c3f637856d54cd8ba8' methods_dict: List[Dict] = self.load_methods(name, commit_sha) # Init project and commits project = Project(project_name=name) commit1 = Commit(sha='commit-1') commit2 = Commit(sha='commit-2') # First add project, and then commits with self.db_helper.create_session() as session: session.add(project) project.commits.append(commit1) project.commits.append(commit2) session.add_all([commit1, commit2]) parser: MethodParser = MethodParser() methods1: List[Tuple[ProdMethod, ProdMethodVersion]] = parser.set_commit( commit1).parse(methods_dict) for method, version in methods1: method.project = project method, _ = session.add(method) version.method = method session.add(version) methods2: List[Tuple[ProdMethod, ProdMethodVersion]] = parser.set_commit( commit2).parse(methods_dict) for method, version in methods2: method.project = project method, _ = session.add(method) version.method = method session.add(version) assert len(self.db_helper.query(Project).all()) == 1 assert len(self.db_helper.query(Commit).all()) == 2 assert len(self.db_helper.query(ProdMethod).all()) == 1 assert len(self.db_helper.query(ProdMethodVersion).all()) == 2 def test_adding_two_projects_to_database(self): # Given: some coverage information # Project 1 name = 'mid_example' commit_sha = 'df1bc2481a05acc3944cc1c3f637856d54cd8ba8' methods_dict: List[Dict] = self.load_methods(name, commit_sha) parser: MethodParser = MethodParser() # Init project and commits project1 = Project(project_name='p1') commit1 = Commit(sha='commit-1') methods1: List[Tuple[ProdMethod, ProdMethodVersion]] = parser\ .set_commit(commit1)\ .parse(methods_dict) project2 = Project(project_name='p2') commit2 = Commit(sha='commit-2') methods2: List[Tuple[ProdMethod, ProdMethodVersion]] = parser\ .set_commit(commit2)\ .parse(methods_dict) # When storing the data in the database parser.store(self.db_helper, project1, commit1, methods1) parser.store(self.db_helper, project2, commit2, methods2) # Then: assert len(self.db_helper.query(Project).all()) == 2 assert len(self.db_helper.query(Commit).all()) == 2 assert len(self.db_helper.query(ProdMethod).all()) == 2 assert len(self.db_helper.query(ProdMethodVersion).all()) == 2