示例#1
0
    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()
示例#2
0
    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)
示例#4
0
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
示例#5
0
 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
示例#6
0
    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()
示例#7
0
    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)
示例#8
0
    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()
示例#9
0
 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))
示例#10
0
    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)
示例#11
0
    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()
示例#12
0
    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
示例#13
0
 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)
示例#15
0
    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()
示例#16
0
    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)