From 5c3ad368890ba6c28df26f22c2cd36ea5d701a87 Mon Sep 17 00:00:00 2001 From: Nina 'Nino' Agrawal Date: Tue, 21 Jan 2025 13:50:33 -0500 Subject: [PATCH] feat: implemented delete function. --Modified plugin versions to be stored as a version and not a string. --Updated tests to internal_delete --Edited the comment formatting Signed-off-by: Nina 'Nino' Agrawal --- hipcheck/src/cache/plugin.rs | 494 ++++++++++++++++++++++++++++++----- 1 file changed, 422 insertions(+), 72 deletions(-) diff --git a/hipcheck/src/cache/plugin.rs b/hipcheck/src/cache/plugin.rs index 79c75d45a..320ea2d73 100644 --- a/hipcheck/src/cache/plugin.rs +++ b/hipcheck/src/cache/plugin.rs @@ -1,26 +1,32 @@ // SPDX-License-Identifier: Apache-2.0 -#![allow(unused)] -use crate::{cache::repo, error::Result, plugin::PluginId, StdResult}; +#![allow(unused)] // for ease of testing +use crate::{ + cache::repo, + error::Result, + hc_error, + plugin::{PluginId, PluginName, PluginPublisher, PluginVersion}, + StdResult, +}; +use dialoguer::Confirm; use pathbuf::pathbuf; +use regex::Regex; +use semver::{Version, VersionReq}; use std::{ borrow::Borrow, - env, + env, fmt, path::{Component, Path, PathBuf}, time::{Duration, SystemTime}, }; use tabled::{Table, Tabled}; use walkdir::{DirEntry, WalkDir}; -//use super::super::cli::CliConfig; -use regex::Regex; //used for regex -use semver::{Version, VersionReq}; -///enums used for sorting are the same as the ones used for listing repo cache entries except for +/// enums used for sorting are the same as the ones used for listing repo cache entries except for /// does not include Largest #[derive(Debug, Clone)] pub enum PluginCacheSort { - OldestDate, //same as "Oldest" in repo.rs + OldestDate, // same as "Oldest" in repo.rs Alpha, - NewestVersion, //sorts the versions of the plugins from newest to oldest unless inverted + NewestVersion, // sorts the versions of the plugins from newest to oldest. Ex: 0.2.1 would come before 0.2.0 } #[derive(Debug, Clone)] @@ -44,13 +50,13 @@ pub struct PluginCacheListScope { pub struct PluginCacheEntry { pub publisher: String, pub name: String, - pub version: String, + pub version: Version, #[tabled(display_with("Self::display_modified", self), rename = "last_modified")] pub modified: SystemTime, } impl PluginCacheEntry { - //copied the function from repo for simplicity + // copied the function from repo for simplicity pub fn display_modified(&self) -> String { let Ok(dur) = self.modified.duration_since(SystemTime::UNIX_EPOCH) else { return "".to_owned(); @@ -75,11 +81,11 @@ impl HcPluginCacheIterator { fn new(path: &Path) -> Self { HcPluginCacheIterator { wd: Box::new( - WalkDir::new(path) //builds a new iterator using WalkDir - .min_depth(3) //makes sure to get the publisher, name, and version folders + WalkDir::new(path) // builds a new iterator using WalkDir + .min_depth(3) // makes sure to get the publisher, name, and version folders .max_depth(3) .into_iter() - .filter_entry(|e| e.path().is_dir()), //makes sure the path is a directory + .filter_entry(|e| e.path().is_dir()), // makes sure the path is a directory ), } } @@ -88,18 +94,22 @@ impl HcPluginCacheIterator { .components() .filter_map(|component| { if let Component::Normal(name) = component { - //extracts components separated by "/" from the path - name.to_str().map(|s| s.to_string()) //converts the string slices into Strings + // extracts components separated by "/" from the path + name.to_str().map(|s| s.to_string()) // converts the string slices into Strings } else { None } }) - .collect(); //collects filepath into vector - //Todo: add error handling for when the path length is less than 3 + .collect(); // collects filepath into a vector + if components.len() < 3 { + return Err(hc_error!("error, path length is less than 3")); + } let relevant_components: &[String] = &components[components.len() - 3..]; let plugin_publisher = relevant_components[0].to_owned(); let plugin_name = relevant_components[1].to_owned(); - let plugin_version = relevant_components[2].to_owned(); + let default_version = Version::parse("0.0.0").unwrap(); + let plugin_version = + Version::parse(relevant_components[2].to_owned().as_str()).unwrap_or(default_version); let last_modified: SystemTime = repo::get_last_modified_or_now(path); Ok(PluginCacheEntry { publisher: plugin_publisher, @@ -110,13 +120,13 @@ impl HcPluginCacheIterator { } } -///Function will take the full file path and put the last 3 directories (publisher, plugin, and review) -///into the Plugin Cache entry +/// Function will take the full file path and put the last 3 directories (publisher, plugin, and review) +/// into the Plugin Cache entry impl Iterator for HcPluginCacheIterator { type Item = PluginCacheEntry; fn next(&mut self) -> Option { if let Some(dir_entry_option) = self.wd.next() { - let dir_entry = dir_entry_option.ok()?; //extracts direntry after converting Result to Option + let dir_entry = dir_entry_option.ok()?; // extracts direntry after converting Result to Option self.path_to_plugin_entry(dir_entry.path()).ok() } else { None @@ -126,7 +136,7 @@ impl Iterator for HcPluginCacheIterator { /// Plugins are stored with the following format `///` pub struct HcPluginCache { - path: PathBuf, //path to the root of the plugin cache + path: PathBuf, // path to the root of the plugin cache entries: Vec, } @@ -140,6 +150,16 @@ impl HcPluginCache { entries, } } + /// Initialization function intended to be used only when testing + pub fn new_for_test(path: &Path, entries: Vec) -> Self { + let plugins_path = pathbuf![path]; + let entries: Vec = entries; + Self { + path: plugins_path, + entries, + } + } + /// The folder in which a specific PluginID will be stored /// /// `///` @@ -156,7 +176,7 @@ impl HcPluginCache { pub fn plugin_kdl(&self, plugin_id: &PluginId) -> PathBuf { self.plugin_download_dir(plugin_id).join("plugin.kdl") } - ///Sort function is the same as in repo.cache but has been modified to get rid of the enum variant Largest + /// Sort function is the same as in repo.cache but has been modified to get rid of the enum variant Largest fn sort>(entries: &mut [A], sort: PluginCacheSort, invert: bool) { // Generic allows sort to handle both owned and borrowed lists let sort_func: fn(&PluginCacheEntry, &PluginCacheEntry) -> std::cmp::Ordering = @@ -169,39 +189,123 @@ impl HcPluginCache { (PluginCacheSort::OldestDate, true) => { |a, b| b.modified.partial_cmp(&a.modified).unwrap() } - //calls a helper function that will parse strings into versions before comparing + // higher versions will appear first when using the Newest Version filter so the partial_cmp parameters must be reversed (PluginCacheSort::NewestVersion, false) => { - |a, b| Self::compare_versions(a, b, false) + |a, b| b.version.partial_cmp(&a.version).unwrap() + } + (PluginCacheSort::NewestVersion, true) => { + |a, b| a.version.partial_cmp(&b.version).unwrap() } - (PluginCacheSort::NewestVersion, true) => |a, b| Self::compare_versions(a, b, true), }; entries.sort_by(|a1: &A, a2: &A| sort_func(a1.borrow(), a2.borrow())); } - fn compare_versions( - a: &PluginCacheEntry, - b: &PluginCacheEntry, - invert: bool, - ) -> std::cmp::Ordering { - let a_version = Version::parse(a.version.as_str()).unwrap(); - let b_version = Version::parse(b.version.as_str()).unwrap(); - if !invert { - b_version.partial_cmp(&a_version).unwrap() //need to reverse the parameters to order the versions from newest to oldest - } else { - a_version.partial_cmp(&b_version).unwrap() + /// All functions up to list involve deleting entries + pub fn delete( + &mut self, + scope: PluginCacheDeleteScope, + filter: Option, + force: bool, + ) -> Result<()> { + let partitioned_vectors = + HcPluginCache::delete_inner(&mut self.entries, scope, filter, force); + let to_del = partitioned_vectors.0; + let to_keep = partitioned_vectors.1; + if !to_del.is_empty() { + if !force { + // Ask user for confirmation + println!("You will delete the following entries:"); + self.display(to_del.iter().collect()); + let conf = Confirm::new() + .with_prompt("Are you sure you want to delete?") + .interact() + .unwrap(); + if !conf { + // Cleanup by returning entries to storage + self.entries.extend(to_del); + self.entries.extend(to_keep); + return Ok(()); + } + } + // Delete entries, returning failures back to the self.entries list + for entry in to_del { + if let Err(e) = self.internal_delete(&entry) { + println!("Failed to delete entry '{}': {e}", entry.name); + self.entries.push(entry) + } + } + self.entries.extend(to_keep); + // self.entries contains `to_keep` plus any entries that were unsuccessfully + // deleted } + Ok(()) + } + /// modified internal_delete function from repo.rs + fn internal_delete(&mut self, entry: &PluginCacheEntry) -> Result<()> { + std::fs::remove_dir_all(self.get_path_to_plugin(entry))?; + Ok(()) + } + /// helper function gets path for internal_delete + fn get_path_to_plugin(&self, entry: &PluginCacheEntry) -> PathBuf { + let plugin_id = PluginId::new( + PluginPublisher(entry.publisher.to_string()), + PluginName(entry.name.to_string()), + PluginVersion(entry.version.to_string()), + ); + let path_to_plugin = self.plugin_download_dir(&plugin_id); + let full_path = pathbuf![&self.path, &path_to_plugin]; + full_path + } + /// created an inner function for ease of testing and to return the partitioned lists + fn delete_inner( + entries: &mut Vec, + scope: PluginCacheDeleteScope, + filter: Option, + force: bool, + ) -> (Vec, Vec) { + // Parse filter to regex if provided + let opt_pat: Option = + filter.map(|raw_p| Regex::new(format!("^{raw_p}$").as_str()).unwrap()); + // similar function to repo.rs but reverses the to_del and to_keep for simplicity + let (to_del, to_keep): (Vec, Vec) = match scope { + PluginCacheDeleteScope::All => entries.drain(0..).partition(|e| match &opt_pat { + Some(pat) => pat.is_match(e.name.as_str()), + None => true, + }), + PluginCacheDeleteScope::Group { sort, invert, n } => { + // First sort entries in-place in entries + HcPluginCache::sort(entries, sort, invert); + let mut hits = 0; + // Now get the first N entries that pass filter + entries.drain(0..).partition(|e| { + let del = match &opt_pat { + Some(pat) => pat.is_match(e.name.as_str()), + None => true, + }; + // passes filter and below max threshold, delete + if del && hits < n { + hits += 1; + true + // put in do_keep + } else { + false + } + }) + } + }; + (to_del, to_keep) } - ///lists all the plugin entries. Works the same as the function in repo.rs except for - ///user can filter plugins by version and publisher + /// lists all the plugin entries. Works the same as the function in repo.rs except for + /// user can filter plugins by version and publisher pub fn list( mut self, scope: PluginCacheListScope, name: Option, publisher: Option, - version: Option, + version: Option, ) -> Result<()> { - //using borrowed data in list_inner in order to avoid transferring ownership + // using borrowed data in list_inner in order to avoid transferring ownership let filtered_entries = HcPluginCache::list_inner(&self.entries, scope, name, publisher, version); match filtered_entries { @@ -215,35 +319,29 @@ impl HcPluginCache { scope: PluginCacheListScope, name: Option, publisher: Option, - version: Option, + version: Option, ) -> Result> { let opt_pat: Option = match name { - //converts the string to a regex expression + // converts the string to a regex expression Some(raw_p) => Some(Regex::new(format!("^{raw_p}$").as_str())?), None => None, }; - //filters based on the regex pattern + // filters based on the regex pattern let mut filt_entries = entries .iter() .filter(|e| match &opt_pat { Some(pat) => pat.is_match(e.name.as_str()), - None => true, //if there is no regex pattern passed in, the default is true + None => true, // if there is no regex pattern passed in, the default is true }) .filter(|e| match &publisher { - Some(a) => e.publisher == *a, //must dereference the borrowed value of a to compare it to the plugin's publisher + Some(a) => e.publisher == *a, // must dereference the borrowed value of a to compare it to the plugin's publisher None => true, }) .filter(|e| match &version { - Some(a) => { - let version_to_compare = VersionReq::parse(a.as_str()) - .expect("String cannot be converted to Version Requirement"); - let plugin_version = Version::parse(e.version.as_str()) - .expect("String cannot be converted to version"); - version_to_compare.matches(&plugin_version) - } + Some(a) => a.matches(&e.version), None => true, }) - .collect::>(); //creates borrowed data to pass to display + .collect::>(); // creates borrowed data to pass to display // Sort filtered entries HcPluginCache::sort(&mut filt_entries, scope.sort, scope.invert); @@ -260,35 +358,32 @@ impl HcPluginCache { println!("{}", Table::new(to_show)); } } - #[cfg(test)] mod tests { - use base64::display; - use super::*; fn test_data() -> Vec { let cache_entry_1 = PluginCacheEntry { publisher: String::from("randomhouse"), name: String::from("bugs bunny"), - version: String::from("0.2.0"), + version: Version::parse("0.2.0").unwrap(), modified: SystemTime::UNIX_EPOCH + Duration::from_secs(3), }; let cache_entry_2 = PluginCacheEntry { publisher: String::from("mitre"), name: String::from("affiliation"), - version: String::from("0.1.0"), + version: Version::parse("0.1.0").unwrap(), modified: SystemTime::UNIX_EPOCH + Duration::from_secs(1), }; let cache_entry_3 = PluginCacheEntry { publisher: String::from("mitre2"), name: String::from("activity"), - version: String::from("0.1.1"), + version: Version::parse("0.1.1").unwrap(), modified: SystemTime::UNIX_EPOCH + Duration::from_secs(2), }; let cache_entry_4 = PluginCacheEntry { publisher: String::from("mitre"), name: String::from("difference"), - version: String::from("0.0.5"), + version: Version::parse("0.0.5").unwrap(), modified: SystemTime::now(), }; vec![cache_entry_1, cache_entry_2, cache_entry_3, cache_entry_4] @@ -331,34 +426,34 @@ mod tests { invert: true, n: None, }; - //Time sort + // Time sort let entries = &test_data(); let results = HcPluginCache::list_inner(entries, time_sort, None, None, None).unwrap(); let expected_output = vec!["affiliation", "activity", "bugs bunny", "difference"]; let mut actual_output: Vec<&String> = results.iter().map(|x| &x.name).collect(); assert_eq!(actual_output, expected_output); - //Reverse time sort + // Reverse time sort let results = HcPluginCache::list_inner(entries, reverse_time_sort, None, None, None).unwrap(); let expected_output = vec!["difference", "bugs bunny", "activity", "affiliation"]; let mut actual_output: Vec<&String> = results.iter().map(|x| &x.name).collect(); assert_eq!(actual_output, expected_output); - //Alphabetical sort + // Alphabetical sort let results = HcPluginCache::list_inner(entries, alpha_sort, None, None, None).unwrap(); let expected_output = vec!["activity", "affiliation", "bugs bunny", "difference"]; let mut actual_output: Vec<&String> = results.iter().map(|x| &x.name).collect(); assert_eq!(actual_output, expected_output); - //Reverse alphabetical sort + // Reverse alphabetical sort let results = HcPluginCache::list_inner(entries, reverse_alpha_sort, None, None, None).unwrap(); let expected_output = vec!["difference", "bugs bunny", "affiliation", "activity"]; let mut actual_output: Vec<&String> = results.iter().map(|x| &x.name).collect(); assert_eq!(actual_output, expected_output); - //Reverse time sort with only two results + // Reverse time sort with only two results let results = HcPluginCache::list_inner(entries, reverse_time_sort_with_two, None, None, None) .unwrap(); @@ -366,13 +461,13 @@ mod tests { let mut actual_output: Vec<&String> = results.iter().map(|x| &x.name).collect(); assert_eq!(actual_output, expected_output); - //Version sort (newest to oldest) + // Version sort (newest to oldest) let results = HcPluginCache::list_inner(entries, version_sort, None, None, None).unwrap(); let expected_output = vec!["bugs bunny", "activity", "affiliation", "difference"]; let mut actual_output: Vec<&String> = results.iter().map(|x| &x.name).collect(); assert_eq!(actual_output, expected_output); - //Reverse version sort + // Reverse version sort let results = HcPluginCache::list_inner(entries, reverse_version_sort, None, None, None).unwrap(); let expected_output: Vec<&str> = @@ -395,7 +490,7 @@ mod tests { alpha_sort, None, None, - Some(String::from(">0.0.5")), + Some(VersionReq::parse(">0.0.5").unwrap()), ) .unwrap(); let expected_output = vec!["activity", "affiliation", "bugs bunny"]; @@ -412,7 +507,7 @@ mod tests { alpha_sort_2, None, None, - Some(String::from("<=0.1.0")), + Some(VersionReq::parse("<=0.1.0").unwrap()), ) .unwrap(); let expected_output = vec!["affiliation", "difference"]; @@ -429,7 +524,7 @@ mod tests { alpha_sort_3, None, None, - Some(String::from("<=0.2.0")), + Some(VersionReq::parse("<=0.2.0").unwrap()), ) .unwrap(); let expected_output = vec!["activity", "affiliation", "bugs bunny", "difference"]; @@ -496,4 +591,259 @@ mod tests { let mut actual_output: Vec<&String> = results.iter().map(|x| &x.name).collect(); assert_eq!(actual_output, expected_output); } + /// Tests for inner delete logic + #[test] + fn test_delete_scope_all() { + let delete_all = PluginCacheDeleteScope::All; + let entries = &mut test_data(); + let mut expected_keep = ["affiliation", "bugs bunny", "difference"]; + let mut expected_delete = ["activity"]; + let delete_function = + HcPluginCache::delete_inner(entries, delete_all, Some(String::from("activity")), false); + let mut actual_delete: Vec<&String> = delete_function.0.iter().map(|x| &x.name).collect(); + let mut actual_keep: Vec<&String> = delete_function.1.iter().map(|x| &x.name).collect(); + actual_delete.sort(); // sorting for ease of testing + actual_keep.sort(); + assert_eq!(actual_delete, expected_delete); + assert_eq!(actual_keep, expected_keep); + } + #[test] + fn test_delete_sort_by_alphabetical() { + let delete_alpha = PluginCacheDeleteScope::Group { + sort: PluginCacheSort::Alpha, + invert: false, + n: 2, + }; + let entries = &mut test_data(); + let mut expected_keep = ["bugs bunny", "difference"]; + let mut expected_delete = ["activity", "affiliation"]; + let delete_function = + HcPluginCache::delete_inner(entries, delete_alpha, Some(String::from("a.*")), false); + let mut actual_delete: Vec<&String> = delete_function.0.iter().map(|x| &x.name).collect(); + let mut actual_keep: Vec<&String> = delete_function.1.iter().map(|x| &x.name).collect(); + assert_eq!(actual_keep, expected_keep); + + // test with n = 1 + let delete_alpha = PluginCacheDeleteScope::Group { + sort: PluginCacheSort::Alpha, + invert: false, + n: 1, + }; + let entries = &mut test_data(); + let mut expected_keep = ["affiliation", "bugs bunny", "difference"]; + let mut expected_delete = ["activity"]; + let delete_function = + HcPluginCache::delete_inner(entries, delete_alpha, Some(String::from("a.*")), false); + let mut actual_delete: Vec<&String> = delete_function.0.iter().map(|x| &x.name).collect(); + let mut actual_keep: Vec<&String> = delete_function.1.iter().map(|x| &x.name).collect(); + assert_eq!(actual_delete, expected_delete); + assert_eq!(actual_keep, expected_keep); + + // test with reverse + let delete_alpha = PluginCacheDeleteScope::Group { + sort: PluginCacheSort::Alpha, + invert: true, + n: 2, + }; + let entries = &mut test_data(); + let mut expected_keep = ["difference", "bugs bunny"]; + let mut expected_delete = ["affiliation", "activity"]; + let delete_function = + HcPluginCache::delete_inner(entries, delete_alpha, Some(String::from("a.*")), false); + let mut actual_delete: Vec<&String> = delete_function.0.iter().map(|x| &x.name).collect(); + let mut actual_keep: Vec<&String> = delete_function.1.iter().map(|x| &x.name).collect(); + assert_eq!(actual_delete, expected_delete); + assert_eq!(actual_keep, expected_keep); + } + #[test] + fn test_delete_sort_by_date() { + let delete_date = PluginCacheDeleteScope::Group { + sort: PluginCacheSort::OldestDate, + invert: false, + n: 2, + }; + let entries = &mut test_data(); + let mut expected_keep = ["bugs bunny", "difference"]; + let mut expected_delete = ["affiliation", "activity"]; + let delete_function = + HcPluginCache::delete_inner(entries, delete_date, Some(String::from("a.*")), false); + let mut actual_delete: Vec<&String> = delete_function.0.iter().map(|x| &x.name).collect(); + let mut actual_keep: Vec<&String> = delete_function.1.iter().map(|x| &x.name).collect(); + assert_eq!(actual_keep, expected_keep); + + // test with n = 1 + let delete_date = PluginCacheDeleteScope::Group { + sort: PluginCacheSort::OldestDate, + invert: false, + n: 1, + }; + let entries = &mut test_data(); + let mut expected_keep = ["activity", "bugs bunny", "difference"]; + let mut expected_delete = ["affiliation"]; + let delete_function = + HcPluginCache::delete_inner(entries, delete_date, Some(String::from("a.*")), false); + let mut actual_delete: Vec<&String> = delete_function.0.iter().map(|x| &x.name).collect(); + let mut actual_keep: Vec<&String> = delete_function.1.iter().map(|x| &x.name).collect(); + assert_eq!(actual_delete, expected_delete); + assert_eq!(actual_keep, expected_keep); + + // test with reverse + let delete_date = PluginCacheDeleteScope::Group { + sort: PluginCacheSort::OldestDate, + invert: true, + n: 2, + }; + let entries = &mut test_data(); + let mut expected_keep = ["difference", "bugs bunny"]; + let mut expected_delete = ["activity", "affiliation"]; + let delete_function = + HcPluginCache::delete_inner(entries, delete_date, Some(String::from("a.*")), false); + let mut actual_delete: Vec<&String> = delete_function.0.iter().map(|x| &x.name).collect(); + let mut actual_keep: Vec<&String> = delete_function.1.iter().map(|x| &x.name).collect(); + assert_eq!(actual_delete, expected_delete); + assert_eq!(actual_keep, expected_keep); + } + #[test] + fn test_delete_sort_by_version() { + let delete_version = PluginCacheDeleteScope::Group { + sort: PluginCacheSort::NewestVersion, + invert: false, + n: 2, + }; + let entries = &mut test_data(); + let mut expected_keep = ["bugs bunny", "difference"]; + let mut expected_delete = ["activity", "affiliation"]; + let delete_function = + HcPluginCache::delete_inner(entries, delete_version, Some(String::from("a.*")), false); + let mut actual_delete: Vec<&String> = delete_function.0.iter().map(|x| &x.name).collect(); + let mut actual_keep: Vec<&String> = delete_function.1.iter().map(|x| &x.name).collect(); + assert_eq!(actual_keep, expected_keep); + + // test with n = 1 + let delete_version = PluginCacheDeleteScope::Group { + sort: PluginCacheSort::NewestVersion, + invert: false, + n: 1, + }; + let entries = &mut test_data(); + let mut expected_keep = ["bugs bunny", "affiliation", "difference"]; + let mut expected_delete = ["activity"]; + let delete_function = + HcPluginCache::delete_inner(entries, delete_version, Some(String::from("a.*")), false); + let mut actual_delete: Vec<&String> = delete_function.0.iter().map(|x| &x.name).collect(); + let mut actual_keep: Vec<&String> = delete_function.1.iter().map(|x| &x.name).collect(); + assert_eq!(actual_delete, expected_delete); + assert_eq!(actual_keep, expected_keep); + + // test with reverse + let delete_version = PluginCacheDeleteScope::Group { + sort: PluginCacheSort::NewestVersion, + invert: true, + n: 2, + }; + let entries = &mut test_data(); + let mut expected_keep = ["difference", "bugs bunny"]; + let mut expected_delete = ["affiliation", "activity"]; + let delete_function = + HcPluginCache::delete_inner(entries, delete_version, Some(String::from("a.*")), false); + let mut actual_delete: Vec<&String> = delete_function.0.iter().map(|x| &x.name).collect(); + let mut actual_keep: Vec<&String> = delete_function.1.iter().map(|x| &x.name).collect(); + assert_eq!(actual_delete, expected_delete); + assert_eq!(actual_keep, expected_keep); + } + /// tests for when you provide no filter + #[test] + fn test_delete_with_no_filter() { + let delete_all = PluginCacheDeleteScope::All; + let entries = &mut test_data(); + let mut expected_keep: Vec<&str> = Vec::new(); + let mut expected_delete = ["activity", "affiliation", "bugs bunny", "difference"]; + let delete_function = HcPluginCache::delete_inner(entries, delete_all, None, false); + let mut actual_delete: Vec<&String> = delete_function.0.iter().map(|x| &x.name).collect(); + let mut actual_keep: Vec<&String> = delete_function.1.iter().map(|x| &x.name).collect(); + actual_delete.sort(); + assert_eq!(actual_delete, expected_delete); + assert_eq!(actual_keep, expected_keep); + + /// Running the no_filter test where the scope is Some. Ensures that nothing should be deleted regardless of the scope. + // Version sort + let delete_some_version = PluginCacheDeleteScope::Group { + sort: PluginCacheSort::NewestVersion, + invert: false, + n: 4, + }; + let entries = &mut test_data(); + let mut expected_keep: Vec<&str> = Vec::new(); + let mut expected_delete = vec!["bugs bunny", "activity", "affiliation", "difference"]; + let delete_function = + HcPluginCache::delete_inner(entries, delete_some_version, None, false); + let mut actual_delete: Vec<&String> = delete_function.0.iter().map(|x| &x.name).collect(); + let mut actual_keep: Vec<&String> = delete_function.1.iter().map(|x| &x.name).collect(); + actual_keep.sort(); + assert_eq!(actual_delete, expected_delete); + assert_eq!(actual_keep, expected_keep); + + // Date sort + let delete_some_date = PluginCacheDeleteScope::Group { + sort: PluginCacheSort::OldestDate, + invert: false, + n: 4, + }; + let entries = &mut test_data(); + let mut expected_keep: Vec<&str> = Vec::new(); + let mut expected_delete = vec!["affiliation", "activity", "bugs bunny", "difference"]; + let delete_function = HcPluginCache::delete_inner(entries, delete_some_date, None, false); + let mut actual_delete: Vec<&String> = delete_function.0.iter().map(|x| &x.name).collect(); + let mut actual_keep: Vec<&String> = delete_function.1.iter().map(|x| &x.name).collect(); + assert_eq!(actual_delete, expected_delete); + assert_eq!(actual_keep, expected_keep); + + // Alphabetical sort + let delete_some_alpha = PluginCacheDeleteScope::Group { + sort: PluginCacheSort::Alpha, + invert: false, + n: 4, + }; + let entries = &mut test_data(); + let mut expected_keep: Vec<&str> = Vec::new(); + let mut expected_delete = ["activity", "affiliation", "bugs bunny", "difference"]; + let delete_function = HcPluginCache::delete_inner(entries, delete_some_alpha, None, false); + let mut actual_delete: Vec<&String> = delete_function.0.iter().map(|x| &x.name).collect(); + let mut actual_keep: Vec<&String> = delete_function.1.iter().map(|x| &x.name).collect(); + assert_eq!(actual_delete, expected_delete); + assert_eq!(actual_keep, expected_keep); + } + /// tests that the path given to delete_internal() is the correct one. + #[test] + fn test_get_correct_path_to_deletion() { + let path = std::env::current_dir().unwrap(); + let entries = test_data(); + let mut hc_plugin_cache = HcPluginCache::new_for_test(path.as_path(), entries); + let entry = &test_data()[0]; + let actual_path = hc_plugin_cache.get_path_to_plugin(entry); + let mut expected_path = hc_plugin_cache.path; + let expected_path = pathbuf![ + &expected_path, + &entry.publisher, + &entry.name, + &entry.version.to_string() + ]; + assert_eq!(actual_path, expected_path); + + // Test for incorrect case, wrong entry being passed to get_actual_path + let path = std::env::current_dir().unwrap(); + let entries = test_data(); + let mut hc_plugin_cache = HcPluginCache::new_for_test(path.as_path(), entries); + let correct_entry = &test_data()[0]; + let incorrect_entry = &test_data()[1]; + let actual_path = hc_plugin_cache.get_path_to_plugin(incorrect_entry); + let mut expected_path = hc_plugin_cache.path; + let expected_path = pathbuf![ + &expected_path, + &correct_entry.publisher, + &correct_entry.name, + &correct_entry.version.to_string() + ]; + assert_ne!(actual_path, expected_path); + } }