def test_add(helpers, pcs_full, valid_profile_pool): """Test calling 'perun add profile hash', i.e. basic functionality Expecting no error. Profile is added to the repository, and to the index, to the specified minor version. """ git_repo = git.Repo(os.path.split(pcs_full.path)[0]) commits = [binascii.hexlify(c.binsha).decode('utf-8') for c in git_repo.iter_commits()] current_head = commits[0] before_count = helpers.count_contents_on_path(pcs_full.path) obj_path = pcs_full.path # First valid profile should be mapped to the same chunk for valid_profile in valid_profile_pool: valid_profile = helpers.prepare_profile(pcs_full, valid_profile, current_head) # Check that the profile was NOT in the index before before_entries_count = assert_before_add(helpers, obj_path, current_head, valid_profile) # Add the profile to timestamp commands.add([valid_profile], current_head, keep_profile=True) # Now check, that the profile was successfully added to index, and its entry is valid after_entries_count = assert_after_valid_add(helpers, obj_path, current_head, valid_profile) assert before_entries_count == (after_entries_count - 1) # Assert that just len-1 blobs was added, as the second profile has the same structure as # one of the profiles already in the tracking after_count = helpers.count_contents_on_path(pcs_full.path) assert before_count[1] == (after_count[1] - (len(valid_profile_pool) - 1))
def test_add_no_minor(helpers, pcs_full, valid_profile_pool): """Test calling 'perun add profile hash' without specified minor version Expecting no error. Profiles are added to the repository, and to the index, to the head. Fixme: Extend with more checks """ git_repo = git.Repo(os.path.split(pcs_full.path)[0]) head = str(git_repo.head.commit) before_count = helpers.count_contents_on_path(pcs_full.path) obj_path = pcs_full.path for valid_profile in valid_profile_pool: valid_profile = helpers.prepare_profile(pcs_full, valid_profile, head) # Check that the profile was NOT in the index before before_entries_count = assert_before_add(helpers, obj_path, head, valid_profile) commands.add([valid_profile], None, keep_profile=True) # Now check, that the profile was successfully added to index, and its entry is valid after_entries_count = assert_after_valid_add(helpers, obj_path, head, valid_profile) assert before_entries_count == (after_entries_count - 1) # Assert that just len-1 blobs was added, as the second profile has the same structure as # one of the profiles already in the tracking after_count = helpers.count_contents_on_path(pcs_full.path) assert before_count[1] == (after_count[1] - (len(valid_profile_pool) - 1))
def test_add_wrong_profile(helpers, pcs_full, error_profile_pool, capsys): """Test calling 'perun add profile hash' with profile in wrong format Expecting raising an exception, that the profile is wrong. """ git_repo = git.Repo(os.path.split(pcs_full.get_path())[0]) head = str(git_repo.head.commit) before_count = helpers.count_contents_on_path(pcs_full.get_path()) for error_profile in error_profile_pool: before_entries_count = assert_before_add(helpers, pcs_full.get_path(), head, error_profile) with pytest.raises(IncorrectProfileFormatException): commands.add([error_profile], None, keep_profile=True) # Assert that the profile was not added into the index after_entries_count = assert_after_invalid_add(helpers, pcs_full.get_path(), head, error_profile) assert before_entries_count == after_entries_count # Assert that nothing was added (rather weak, but should be enough) after_count = helpers.count_contents_on_path(pcs_full.get_path()) assert before_count == after_count # Try to assert adding not existing profile with pytest.raises(SystemExit): commands.add(['notexisting.perf'], None, keep_profile=True) after_count = helpers.count_contents_on_path(pcs_full.get_path()) assert before_count == after_count _, err = capsys.readouterr() assert "notexisting.perf does not exist" in err
def test_add_outside_pcs(valid_profile_pool): """Test calling 'perun add outside of the scope of the PCS wrapper Expecting an exception NotPerunRepositoryException, as we are outside of the perun scope, and thus should not do anything, should be caught on the CLI/UI level """ with pytest.raises(NotPerunRepositoryException): commands.add([valid_profile_pool[0]], None, keep_profile=True)
def pcs_full(): """ Returns: PCS: object with performance control system, initialized with some files and stuff """ # Change working dir into the temporary directory profiles = stored_profile_pool() pcs_path = tempfile.mkdtemp() os.chdir(pcs_path) commands.init_perun_at(pcs_path, False, {'vcs': { 'url': '../', 'type': 'git' }}) # Construct the PCS object pcs_obj = pcs.PCS(pcs_path) # Initialize git vcs.init('git', pcs_path, {}) # Populate repo with commits repo = git.Repo(pcs_path) # Create first commit file1 = os.path.join(pcs_path, "file1") store.touch_file(file1) repo.index.add([file1]) root = repo.index.commit("root") # Create second commit file2 = os.path.join(pcs_path, "file2") store.touch_file(file2) repo.index.add([file2]) current_head = repo.index.commit("second commit") # Populate PCS with profiles root_profile = Helpers.prepare_profile(pcs_obj, profiles[0], str(root)) commands.add([root_profile], str(root)) chead_profile1 = Helpers.prepare_profile(pcs_obj, profiles[1], str(current_head)) chead_profile2 = Helpers.prepare_profile(pcs_obj, profiles[2], str(current_head)) commands.add([chead_profile1, chead_profile2], str(current_head)) # Assert that we have five blobs: 2 for commits and 3 for profiles pcs_object_dir = os.path.join(pcs_path, ".perun", "objects") number_of_perun_objects = sum( len(os.listdir(os.path.join(pcs_object_dir, sub))) for sub in os.listdir(pcs_object_dir)) assert number_of_perun_objects == 5 yield pcs_obj # clean up the directory shutil.rmtree(pcs_path)
def add(profile, minor, **kwargs): """Links profile to concrete minor version storing its content in the ``.perun`` dir and registering the profile in internal minor version index. In order to link <profile> to given minor version <hash> the following steps are executed: 1. We check in <profile> that its :preg:`origin` key corresponds to <hash>. This serves as a check, that we do not assign profiles to different minor versions. 2. The :preg:`origin` is removed and contents of <profile> are compresed using `zlib` compression method. 3. Binary header for the profile is constructed. 4. Compressed contents are appended to header, and this blob is stored in ``.perun/objects`` directory. 5. New blob is registered in <hash> minor version's index. 6. Unless ``--keep-profile`` is set. The original profile is deleted. If no `<hash>` is specified, then current ``HEAD`` of the wrapped version control system is used instead. Massaging of <hash> is taken care of by underlying version control system (e.g. git uses ``git rev-parse``). <profile> can either be a ``pending tag`` or a fullpath. ``Pending tags`` are in form of ``i@p``, where ``i`` stands for an index in the pending profile directory (i.e. ``.perun/jobs``) and ``@p`` is literal suffix. Run ``perun status`` to see the `tag` anotation of pending profiles. Example of adding profiles: .. code-block:: bash $ perun add mybin-memory-input.txt-2017-03-01-16-11-04.perf This command adds the profile collected by `memory` collector during profiling ``mybin`` command with ``input.txt`` workload on 1st March at 16:11 to the current ``HEAD``. An error is raised if the command is executed outside of range of any perun, if <profile> points to incorrect profile (i.e. not w.r.t. :ref:`profile-spec`) or <hash> does not point to valid minor version ref. See :doc:`internals` for information how perun handles profiles internally. """ try: commands.add(profile, minor, **kwargs) except (NotPerunRepositoryException, IncorrectProfileFormatException) as exception: perun_log.error(str(exception))
def test_add_on_no_vcs(helpers, pcs_without_vcs, valid_profile_pool): """Test calling 'perun add' without having a wrapped vcs Expecting and error, as this will call a wrapper over custom "repo" called pvcs, which is not supported, but is simply a sane default. """ before_count = helpers.count_contents_on_path(pcs_without_vcs.path) assert pcs_without_vcs.vcs_type == 'pvcs' with pytest.raises(UnsupportedModuleException): commands.add([valid_profile_pool[0]], None, keep_profile=True) # Assert that nothing was added (rather weak, but should be enough) after_count = helpers.count_contents_on_path(pcs_without_vcs.path) assert before_count == after_count
def test_add_wrong_minor(helpers, pcs_full, valid_profile_pool): """Test calling 'perun add profile hash' with hash not occuring in wrapped VCS Expecting raising an exception, that the specified minor version is wrong. """ git_repo = git.Repo(os.path.split(pcs_full.path)[0]) commits = [binascii.hexlify(c.binsha).decode('utf-8') for c in git_repo.iter_commits()] wrong_commit = commits[0][:20] + commits[1][20:] assert len(wrong_commit) == 40 assert wrong_commit != commits[0] and wrong_commit != commits[1] before_count = helpers.count_contents_on_path(pcs_full.path) with pytest.raises(VersionControlSystemException): commands.add([valid_profile_pool[0]], wrong_commit, keep_profile=True) # Assert that nothing was added (rather weak, but should be enough) after_count = helpers.count_contents_on_path(pcs_full.path) assert before_count == after_count
def test_add_existing(helpers, pcs_full, valid_profile_pool, capsys): """Test calling 'perun add profile hash', when the profile is already assigned to current Expecting probably to warn the user, that the profile is already assigned and don't change anything or add new redundant entry for that. Fixme: Extend with more checks """ git_repo = git.Repo(os.path.split(pcs_full.get_path())[0]) head = str(git_repo.head.commit) before_count = helpers.count_contents_on_path(pcs_full.get_path()) obj_path = pcs_full.get_path() for valid_profile in valid_profile_pool: valid_profile = helpers.prepare_profile(pcs_full.get_job_directory(), valid_profile, head) # Check that the profile was NOT in the index before before_entries_count = assert_before_add(helpers, obj_path, head, valid_profile) commands.add([valid_profile], None, keep_profile=True) # Assert that the profile was successfully added to the index middle_entries_count = assert_after_valid_add(helpers, obj_path, head, valid_profile) assert before_entries_count == (middle_entries_count - 1) commands.add([valid_profile], None, keep_profile=True) # Assert that nothing was added to the index after_entries_count = assert_after_valid_add(helpers, obj_path, head, valid_profile) assert middle_entries_count == after_entries_count # Check that some kind of message was written to user out, _ = capsys.readouterr() assert 'already registered in' in out # Assert that just len-1 blobs was added, as the second profile has the same structure as # one of the profiles already in the tracking after_count = helpers.count_contents_on_path(pcs_full.get_path()) assert before_count[0] == (after_count[0] - (len(valid_profile_pool) - 1) - 2)
def register_profile_of_minor_version(pcs, minor_version, profile_path, profile_id): """Function for registering performance profile of a minor version :param PCS pcs: object with performance control system wrapper :param str minor_version_sha: minor version SHA :param str profile_path: path to the performance profile :param str profile_id: name of the performance profile :return: new profile path on success, otherwise 404 NOT FOUND """ try: commands.add([profile_path], minor_version, keep_profile=False) profiles = commands.get_minor_version_profiles(pcs, minor_version); for perf_profile in profiles: if (perf_profile.source == profile_id): return jsonify({'path' : perf_profile.realpath}) return create_response('No such performance profile', 404) except Exception as e: eprint(e) return create_response(e, 404)
def store_generated_profile(prof, job): """Stores the generated profile in the pending jobs directory. :param dict prof: profile that we are storing in the repository :param Job job: job with additional information about generated profiles """ full_profile = profile.finalize_profile_for_job(prof, job) full_profile_name = profile.generate_profile_name(full_profile) profile_directory = pcs.get_job_directory() full_profile_path = os.path.join(profile_directory, full_profile_name) profile.store_profile_at(full_profile, full_profile_path) log.info("stored profile at: {}".format( os.path.relpath(full_profile_path))) if dutils.strtobool( str( config.lookup_key_recursively("profiles.register_after_run", "false"))): # We either store the profile according to the origin, or we use the current head dst = prof.get('origin', vcs.get_minor_head()) commands.add([full_profile_path], dst, keep_profile=False)
def test_add_on_empty_repo(helpers, pcs_with_empty_git, valid_profile_pool, capsys): """Test calling 'perun add' on empty repository Expecting an error and system exist as there is no commit, so nothing can be add. """ assert os.getcwd() == os.path.split(pcs_with_empty_git.path)[0] before_count = helpers.count_contents_on_path(pcs_with_empty_git.path) # Assert that the program ends with pytest.raises(SystemExit): commands.add([valid_profile_pool[0]], None, keep_profile=True) # Assert that nothing was added (rather weak, but should be enough) after_count = helpers.count_contents_on_path(pcs_with_empty_git.path) assert before_count == after_count # Assert that the error message is OK _, err = capsys.readouterr() expected = "fatal: could not obtain head minor version: " \ "Reference at 'refs/heads/master' does not exist" assert err.strip() == termcolor.colored(expected, 'red')
def pcs_with_degradations(): """ """ pool_path = os.path.join(os.path.split(__file__)[0], 'degradation_profiles') profiles = [ os.path.join(pool_path, 'linear_base.perf'), os.path.join(pool_path, 'linear_base_degradated.perf'), os.path.join(pool_path, 'quad_base.perf') ] # Change working dir into the temporary directory pcs_path = tempfile.mkdtemp() os.chdir(pcs_path) commands.init_perun_at(pcs_path, False, {'vcs': {'url': '../', 'type': 'git'}}) # Initialize git vcs.init({}) # Populate repo with commits repo = git.Repo(pcs_path) # Create first commit file1 = os.path.join(pcs_path, "file1") store.touch_file(file1) repo.index.add([file1]) root = repo.index.commit("root") # Create second commit repo.git.checkout('-b', 'develop') file2 = os.path.join(pcs_path, "file2") store.touch_file(file2) repo.index.add([file2]) middle_head = repo.index.commit("second commit") # Create third commit repo.git.checkout('master') file3 = os.path.join(pcs_path, "file3") store.touch_file(file3) repo.index.add([file3]) repo.index.commit("parallel commit") repo.git.merge('--no-ff', 'develop') current_head = str(repo.head.commit) # Populate PCS with profiles jobs_dir = pcs.get_job_directory() root_profile = Helpers.prepare_profile(jobs_dir, profiles[0], str(root)) commands.add([root_profile], str(root)) middle_profile = Helpers.prepare_profile(jobs_dir, profiles[1], str(middle_head)) commands.add([middle_profile], str(middle_head)) head_profile = Helpers.prepare_profile(jobs_dir, profiles[2], str(current_head)) commands.add([head_profile], str(current_head)) yield pcs # clean up the directory shutil.rmtree(pcs_path)