def process_project_version(self, project: Project, version: ProjectVersion): self.__logger.info(" - Version : %s", version.version_id) revision = "-" if project.repository.vcstype == "git": revision = version.revision + "~1" elif project.repository.vcstype == "svn": revision = str(int(version.revision) - 1) self.__logger.info(" Revision : %s", revision) checkout = version.get_checkout(self.__checkouts_path) if not checkout.exists(): self.__logger.info(" Checkout : not checked out") else: self.__logger.info(" Checkout : %s", checkout.checkout_dir) version_compile = version.get_compile(self.__compiles_path) if version_compile.needs_compile(): compile_state = "not compiled" else: compile_state = "compiled" self.__logger.info(" Compile : %s", compile_state) super().process_project_version(project, version) return self.ok()
def run(self, version: ProjectVersion): logger = logging.getLogger("tasks.checkout") try: checkout = version.get_checkout(self.checkouts_path) except ValueError as e: raise UserWarning("Checkout data corrupted: %s", e) if self.force_checkout: checkout.delete() checkout_exists = checkout.exists() logger.debug("Checkout exists = %r", checkout_exists) if checkout_exists: logger.debug("Already checked out %s.", version) else: logger.info("Fetching %s from %s...", version, checkout) if self.use_temp_dir: temp_dir = mkdtemp(prefix="mubench-checkout_") temp_checkout = version.get_checkout(temp_dir) temp_checkout.create(self.run_timestamp) logger.debug("Copying checkout to persistent directory...") copy_tree(temp_dir, self.checkouts_path) remove_tree(temp_dir) else: checkout.create(self.run_timestamp) return checkout
def setup(self): self.temp_dir = mkdtemp(prefix="mubench-test-project-version_") self.project_id = "project" self.version_id = "v1" self.uut = ProjectVersion(self.temp_dir, self.project_id, self.version_id)
def create_version(version_id: str, misuses: List[Misuse] = None, meta: Dict[str, Any] = None, project: Project=None): if not project: project = create_project("-project-") version = ProjectVersion(project._base_path, project.id, version_id) version._ProjectVersion__project = project version._MISUSES = [create_misuse("-misuse-")] if misuses is None else misuses version._YAML = meta project._VERSIONS.append(version) return version
def versions(self) -> List[ProjectVersion]: if not self._VERSIONS: if exists(self._versions_path): self._VERSIONS = [ ProjectVersion(self._base_path, self.id, subdir) for subdir in listdir(self._versions_path) if ProjectVersion.is_project_version( join(self._versions_path, subdir)) ] return self._VERSIONS
def process_project_version_misuse(self, project: Project, version: ProjectVersion, misuse: Misuse): self.__logger.info(" - Misuse : %s", misuse.misuse_id) self.__logger.info(" Description : %s", misuse.description.strip()) self.__logger.info(" Fix Description : %s", misuse.fix.description.strip()) self.__logger.info(" Misuse Elements : - %s", misuse.characteristics[0]) for characteristic in misuse.characteristics[1:]: self.__logger.info(" - %s", characteristic) checkout = version.get_checkout(self.__checkouts_path) if checkout.exists(): location = misuse.location if project.repository.vcstype == "synthetic": checkout_path = join(version.path, "compile") else: checkout_path = checkout.checkout_dir source_file_path = join(checkout_path, version.source_dir, location.file) self.__logger.info(" Source File : %s", source_file_path) self.__logger.info(" Enclosing Method : %s", location.method) self.__logger.info(" Fix Diff : %s", misuse.fix.commit) return self.ok()
def process_project_version(self, project: Project, version: ProjectVersion) -> List[str]: logger = logging.getLogger("checkout") try: checkout = version.get_checkout(self.checkouts_path) except ValueError as e: logger.error("Checkout data corrupted: %s", e) return self.skip(version) try: if self.force_checkout: checkout.delete() checkout_exists = checkout.exists() logger.debug("Checkout exists = %r", checkout_exists) if checkout_exists: logger.debug("Already checked out %s.", version) else: logger.info("Fetching %s from %s...", version, checkout) checkout.create() return self.ok() except CommandFailedError as e: logger.error("Checkout failed: %s", e) return self.skip(version) except IOError: logger.error("Checkout failed.", exc_info=True) return self.skip(version)
def process_project_version(self, project: Project, version: ProjectVersion) -> List[str]: logger = self.logger.getChild("version") detector_run = self.experiment.get_run(version) runtime = detector_run.get_runtime() if detector_run.is_success(): logger.info("Preparing findings in %s...", version) result = "success" number_of_findings = detector_run.get_number_of_findings() potential_hits = detector_run.get_potential_hits() logger.info("Found %s potential hits.", len(potential_hits)) else: number_of_findings = 0 potential_hits = [] if detector_run.is_error(): logger.info("Run on %s produced an error.", version) result = "error" elif detector_run.is_timeout(): logger.info("Run on %s timed out.", version) result = "timeout" else: logger.info("Not run on %s.", version) result = "not run" logger.info("Extracting target source code...") version_compile = version.get_compile(self.compiles_base_path) for potential_hit in potential_hits: snippets = potential_hit.get_snippets( version_compile.original_sources_path) potential_hit["target_snippets"] = [ snippet.__dict__ for snippet in snippets ] try: logger.info("Publishing findings...") for potential_hits_slice in self.__slice_by_max_files_per_post( potential_hits): self.__post(project, version, runtime, number_of_findings, result, potential_hits_slice) logger.info("Findings published.") except RequestException as e: response = e.response if response: logger.error("ERROR: %d %s: %s", response.status_code, response.reason, response.text) else: logger.error("ERROR: %s", e) return self.ok()
def _check_misuse_location_exists(self, project: Project, version: ProjectVersion, misuse: Misuse): if "location" in misuse._yaml: location = misuse.location if location.file and location.method: checkout = version.get_checkout(self.checkout_base_path) if not checkout.exists(): self.logger.debug( 'Skipping location check for "{}": requires checkout of "{}".'.format( misuse.id, version.id)) else: source_base_path = join(checkout.checkout_dir, version.source_dir) if not self._location_exists(source_base_path, location.file, location.method): self._report_cannot_find_location(str(location), "{}/misuses/{}/misuse.yml".format(project.id, misuse.misuse_id))
def process_project_version(self, project: Project, version: ProjectVersion) -> List[str]: logger = logging.getLogger("checkout") try: checkout = version.get_checkout(self.checkouts_path) except ValueError as e: logger.error("Checkout data corrupted: %s", e) return self.skip(version) try: if self.force_checkout: checkout.delete() checkout_exists = checkout.exists() logger.debug("Checkout exists = %r", checkout_exists) if checkout_exists: logger.debug("Already checked out %s.", version) else: logger.info("Fetching %s from %s...", version, checkout) if self.use_temp_dir: temp_dir = mkdtemp(prefix="mubench-checkout_") temp_checkout = version.get_checkout(temp_dir) temp_checkout.create() logger.debug("Copying checkout to persistent directory...") copy_tree(temp_dir, self.checkouts_path) remove_tree(temp_dir) else: checkout.create() return self.ok() except CommandFailedError as e: logger.error("Checkout failed: %s", e) return self.skip(version) except IOError: logger.error("Checkout failed.", exc_info=True) return self.skip(version)
def process_project_version(self, project: Project, version: ProjectVersion) -> List[str]: logger = self.logger.getChild("version") detector_run = self.experiment.get_run(version) run_info = detector_run.get_run_info() potential_hits = detector_run.get_potential_hits() if detector_run.is_success(): logger.info("Preparing findings in %s...", version) result = "success" logger.info("Found %s potential hits.", len(potential_hits)) else: if detector_run.is_error(): logger.info("Run on %s produced an error.", version) result = "error" elif detector_run.is_timeout(): logger.info("Run on %s timed out.", version) result = "timeout" else: logger.info("Not run on %s.", version) result = "not run" version_compile = version.get_compile(self.compiles_base_path) try: logger.info("Publishing findings...") for potential_hits_slice in self.__slice_by_max_files_per_post( potential_hits): post_data_slice = [] for potential_hit in potential_hits_slice: postable_data = self._prepare_post(potential_hit, version_compile, logger) post_data_slice.append(postable_data) file_paths = PublishFindingsTask.get_file_paths( potential_hits_slice) self.__post(project, version, run_info, result, post_data_slice, file_paths) logger.info("Findings published.") except RequestException as e: response = e.response if response: logger.error("ERROR: %d %s: %s", response.status_code, response.reason, response.text) else: logger.error("ERROR: %s", e) return self.ok()
def run(self, version: ProjectVersion, checkout: ProjectCheckout): logger = logging.getLogger("task.compile") version_compile = version.get_compile(self.compiles_base_path) build_path = mkdtemp(prefix='mubench-compile_') if self.use_temp_dir else version_compile.build_dir if self.force_compile or checkout.timestamp > version_compile.timestamp: logger.debug("Force compile - removing previous compiles...") version_compile.delete() try: if not version.is_compilable: raise UserWarning("Skipping compilation: not configured.") if version_compile.needs_compile(): logger.info("Compiling %s...", version) logger.debug("Copying checkout to build directory...") checkout_path = checkout.checkout_dir copy_tree(checkout_path, build_path) logger.debug("Copying additional resources...") self.__copy_additional_compile_sources(version, build_path) logger.debug("Compiling project...") self._compile(version.compile_commands, build_path, version_compile.dependencies_path, self.compiles_base_path, logger) logger.debug("Create project jar...") zip_dir_contents(version_compile.original_classes_paths, version_compile.original_classpath) version_compile.save(self.run_timestamp) if self.use_temp_dir: logger.debug("Moving complete build to persistent directory...") copy_tree(build_path, version_compile.build_dir) remove_tree(build_path) except Exception: version_compile.delete() raise return version_compile
def _check_misuse_location_exists(self, version: ProjectVersion, misuse: Misuse): if "location" in misuse._yaml: location = misuse.location if location.file and location.method: checkout = version.get_checkout(self.checkout_base_path) if not checkout or not checkout.exists(): self.logger.debug( 'Skipping location check for "{}": requires checkout of "{}".' .format(misuse.id, version.id)) else: source_base_paths = [ join(checkout.base_path, src_dir) for src_dir in version.source_dirs ] if not self._location_exists( source_base_paths, location.file, location.method): self._report_cannot_find_location( str(location), self._get_rel_misuse_file_path(misuse))
def test_accepts_project_version_directory(self): create_file(self.uut._version_file) assert ProjectVersion.is_project_version(self.uut.path)
def process_project_version(self, project: Project, version: ProjectVersion): logger = logging.getLogger("compile") logger.info("Compiling %s...", version) logger.debug("- Force compile = %r", self.force_compile) logger = logging.getLogger("compile.tasks") project_compile = version.get_compile(self.compiles_base_path) build_path = join(project_compile.base_path, Compile.__BUILD_DIR) sources_path = join(build_path, version.source_dir) classes_path = join(build_path, version.classes_dir) needs_copy_sources = project_compile.needs_copy_sources( ) or self.force_compile needs_compile = project_compile.needs_compile() or self.force_compile if needs_copy_sources or needs_compile: logger.debug("Copying to build directory...") checkout_path = version.get_checkout( self.checkouts_base_path).checkout_dir self.__clean_copy(checkout_path, build_path) logger.debug("Copying additional resources...") self.__copy_additional_compile_sources(version, build_path) if not needs_copy_sources: logger.debug("Already copied project source.") else: try: logger.info("Copying project sources...") self.__clean_copy(sources_path, project_compile.original_sources_path) self.__copy_misuse_sources(sources_path, version.misuses, project_compile.misuse_source_path) except IOError as e: logger.error("Failed to copy project sources: %s", e) return self.skip(version) if not version.compile_commands: logger.warn("Skipping compilation: not configured.") return self.skip(version) if not needs_compile: logger.info("Already compiled project.") else: try: logger.info("Compiling project...") self._compile(version.compile_commands, build_path, project_compile.dependency_base_path) logger.debug("Copying project classes...") self.__clean_copy(classes_path, project_compile.original_classes_path) self.__copy_misuse_classes(classes_path, version.misuses, project_compile.misuse_classes_path) except CommandFailedError as e: logger.error("Compilation failed: %s", e) return self.skip(version) except FileNotFoundError as e: logger.error("Failed to copy classes: %s", e) return self.skip(version) if not version.patterns: logger.info("Skipping pattern compilation: no patterns.") return self.ok() needs_copy_pattern_sources = project_compile.needs_copy_pattern_sources( ) or self.force_compile needs_compile_patterns = project_compile.needs_compile_patterns( ) or self.force_compile if needs_copy_pattern_sources or needs_compile_patterns: logger.debug("Copying to build directory...") checkout_path = version.get_checkout( self.checkouts_base_path).checkout_dir self.__clean_copy(checkout_path, build_path) logger.debug("Copying additional resources...") self.__copy_additional_compile_sources(version, build_path) if not needs_copy_pattern_sources: logger.debug("Already copied pattern sources.") else: try: logger.info("Copying pattern sources...") self.__copy_pattern_sources(version.misuses, project_compile) except IOError as e: logger.error("Failed to copy pattern sources: %s", e) return self.skip(version) if not needs_compile_patterns: logger.info("Already compiled patterns.") else: try: logger.debug("Copying patterns to source directory...") self.__copy(version.patterns, sources_path) logger.info("Compiling patterns...") self._compile(version.compile_commands, build_path, project_compile.dependency_base_path) logger.debug("Copying pattern classes...") self.__copy_pattern_classes(version.misuses, classes_path, project_compile) except FileNotFoundError as e: remove_tree(project_compile.pattern_classes_base_path) logger.error("Compilation failed: %s", e) return self.skip(version) except CommandFailedError as e: logger.error("Compilation failed: %s", e) return self.skip(version) return self.ok()
def process_project_version(self, project: Project, version: ProjectVersion): logger = logging.getLogger("compile") logger.info("Compiling %s...", version) logger = logging.getLogger("compile.tasks") project_compile = version.get_compile(self.compiles_base_path) build_path = mkdtemp( prefix='mubench-compile_' ) if self.use_temp_dir else project_compile.build_dir sources_path = join(build_path, version.source_dir) classes_path = join(build_path, version.classes_dir) if self.force_compile: logger.debug("Force compile - removing previous compiles...") project_compile.delete() try: needs_copy_sources = project_compile.needs_copy_sources() needs_compile = project_compile.needs_compile() if needs_copy_sources or needs_compile: logger.debug("Copying checkout to build directory...") checkout_path = version.get_checkout( self.checkouts_base_path).checkout_dir copy_tree(checkout_path, build_path) logger.debug("Copying additional resources...") self.__copy_additional_compile_sources(version, build_path) if not needs_copy_sources: logger.debug("Already copied source.") else: logger.info("Copying sources...") logger.debug("Copying project sources...") copy_tree(sources_path, project_compile.original_sources_path) logger.debug("Copying misuse sources...") self.__copy_misuse_sources(sources_path, version.misuses, project_compile.misuse_source_path) logger.info("Copying pattern sources...") self.__copy_pattern_sources(version.misuses, project_compile) if not version.compile_commands: logger.warning("Skipping compilation: not configured.") return self.skip(version) if not needs_compile: logger.debug("Already compiled project.") else: logger.info("Compiling project...") logger.debug("Copying patterns to source directory...") self.__copy(version.patterns, sources_path) self._compile(version.compile_commands, build_path, project_compile.dependencies_path, self.compiles_base_path, logger) logger.debug("Move pattern classes...") self.__copy_pattern_classes(version.misuses, classes_path, project_compile) self.__remove_pattern_classes(version.misuses, classes_path) logger.debug("Copy project classes...") copy_tree(classes_path, project_compile.original_classes_path) logger.debug("Create project jar...") self.__create_jar(project_compile.original_classes_path, project_compile.original_classpath) logger.debug("Copy misuse classes...") self.__copy_misuse_classes(classes_path, version.misuses, project_compile.misuse_classes_path) if self.use_temp_dir: logger.debug( "Moving complete build to persistent directory...") copy_tree(build_path, project_compile.build_dir) remove_tree(build_path) except Exception as e: logger.error("Compilation failed: %s", e) project_compile.delete() return self.skip(version) return self.ok()
class TestProjectVersion: # noinspection PyAttributeOutsideInit def setup(self): self.temp_dir = mkdtemp(prefix="mubench-test-project-version_") self.project_id = "project" self.version_id = "v1" self.uut = ProjectVersion(self.temp_dir, self.project_id, self.version_id) def teardown(self): remove_tree(self.temp_dir) def test_sets_path(self): assert_equals( join(self.temp_dir, self.project_id, Project.VERSIONS_DIR, self.version_id), self.uut.path) def test_rejects_non_project_version_directory(self): assert not ProjectVersion.is_project_version(self.temp_dir) def test_accepts_project_version_directory(self): create_file(self.uut._version_file) assert ProjectVersion.is_project_version(self.uut.path) def test_reads_version_file(self): test_dict = {"revision": "42"} with safe_open(self.uut._version_file, 'w+') as stream: yaml.dump(test_dict, stream) assert_equals(test_dict, self.uut._yaml) def test_finds_misuses(self): misuse = create_misuse("1", project=create_project(self.project_id, base_path=self.temp_dir)) create_file(misuse.misuse_file) self.uut._YAML = {"misuses": ["1"]} actual_misuses = self.uut.misuses assert_equals([misuse], actual_misuses) def test_version_without_misuse(self): self.uut._YAML = {"misuses": None} actual_misuses = self.uut.misuses assert_equals([], actual_misuses) def test_finds_only_own_misuses(self): project = create_project(self.project_id, base_path=self.temp_dir) misuse1 = create_misuse("1", project=project) create_file(misuse1.misuse_file) misuse2 = create_misuse("2", project=project) create_file(misuse2.misuse_file) self.uut._YAML = {"misuses": ["2"]} misuses = self.uut.misuses assert_equals([misuse2], misuses) def test_creates_build_config(self): self.uut._YAML = { "build": { "src": "src/java/", "commands": ["mvn compile"], "classes": "target/classes/" } } assert_equals("src/java/", self.uut.source_dir) assert_equals(["mvn compile"], self.uut.compile_commands) assert_equals("target/classes/", self.uut.classes_dir) def test_creates_build_config_with_defaults(self): self.uut._YAML = {} assert_equals("", self.uut.source_dir) assert_equals([], self.uut.compile_commands) assert_equals("", self.uut.classes_dir) def test_id(self): assert_equals("{}.{}".format(self.project_id, self.version_id), self.uut.id) def test_derives_additional_compile_sources_path(self): assert_equals(join(self.uut.path, "compile"), self.uut.additional_compile_sources) def test_derives_compile_base_path(self): self.uut._MISUSES = [create_misuse("m") ] # prevent version from loading misuses project_compile = self.uut.get_compile("/base/path") assert_equals(join("/base/path", self.project_id, self.version_id), project_compile.base_path)
def test_rejects_non_project_version_directory(self): assert not ProjectVersion.is_project_version(self.temp_dir)