Skip to content

Commit

Permalink
Add some testing
Browse files Browse the repository at this point in the history
* top level test_data dir has sub dirs for scenarios, two have been
  created (single cargo, two python)
* restructure kondo/main into kondo/main and lib so integ test can run
* unit test: kondo/discover and kondo-lib/clean
* integration test: cli run of "kondo -- --version" command
* infrastructure for creating a test project to run kondo in
  destructivly
  • Loading branch information
tompscanlan committed Aug 31, 2023
1 parent cc1ba07 commit 92564ad
Show file tree
Hide file tree
Showing 19 changed files with 483 additions and 144 deletions.
46 changes: 45 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions kondo-lib/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,9 @@ license = "MIT"
[dependencies]
ignore = "0.4.18"
walkdir = "2"

# need recursive copy for testing
fs_extra = "1.3.0"

# need temporary test dirs
tempfile = "3.8.0"
33 changes: 32 additions & 1 deletion kondo-lib/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
mod lib_test;

use std::{
borrow::Cow,
error::{self, Error},
fs, path,
fs,
io::Write,
path,
time::SystemTime,
};

Expand Down Expand Up @@ -479,6 +483,33 @@ pub fn path_canonicalise(
Ok(base.join(tail).canonicalize()?)
}
}
// Given a name, create a new simulated python project in a safe to delete directry
pub fn create_fake_python_project(name: String) -> tempfile::TempDir {
// Make a new project in a temporary directory
let tmp_dir = tempfile::tempdir().unwrap();

// make a new root in the tmp dir
let project_dir = tmp_dir.path().join(name);
std::fs::create_dir(&project_dir).unwrap();

// Must have a directory to hold the project.
let cache_dir = project_dir.join("__pycache__");
std::fs::create_dir(&cache_dir).unwrap();

// Must have data in the cache to delete
let mut data_file = std::fs::File::create(cache_dir.join("cache.data")).unwrap();
data_file.write_all(b"#oodles of cache')\n").unwrap();
let mut data_file_b = std::fs::File::create(cache_dir.join("other.cache")).unwrap();
data_file_b.write_all(b"#oodles of cache')\n").unwrap();

// and a file of type .py to signal we're a python project
let mut python_file = std::fs::File::create(project_dir.join("main.py")).unwrap();
python_file
.write_all(b"#!/bin/python\n\nprint('Hello, world!')\n")
.unwrap();

tmp_dir
}

#[cfg(test)]
mod tests {
Expand Down
71 changes: 71 additions & 0 deletions kondo-lib/src/lib_test.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
#[cfg(test)]
mod test {
use std::path::PathBuf;
use tempfile;

use crate::{Project, ProjectType, ScanOptions};

// Given test data, clean should remove some files
#[test]
fn test_clean() {
let scan_options: ScanOptions = ScanOptions {
follow_symlinks: false,
same_file_system: true,
};

let tempdir = get_copy_of_test_data_as_temp_dir();
let path = tempdir.path().join("test_data/scenario_a");

// Given 2 py projects with content cached
let project_a = Project {
path: path.join("python-project-a"),
project_type: ProjectType::Python,
};
let _project_b = Project {
path: path.join("python-project-b"),
project_type: ProjectType::Python,
};

assert!(
project_a.size(&scan_options) > 0,
"size of project ought to be greater than 0"
);
assert!(project_a.path.exists(), "project ought to exist");

// Run clean and check before and after that file exists and is deleted
assert!(
project_a.path.join("__pycache__/1").exists(),
"cache file ought to exist"
);
Project::clean(&project_a);
assert!(
!project_a.path.join("__pycache__/1").exists(),
"cache file should have been deleted"
);

assert!(project_a.path.exists(), "project ought to still exist");

// clean up
tempdir.close().unwrap();
}

// copy the test_data dir to a ephemeral temp dir for destructive testing
fn get_copy_of_test_data_as_temp_dir() -> tempfile::TempDir {
extern crate fs_extra;
let options = fs_extra::dir::CopyOptions::new();

let mut path: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
path.push("../test_data");
println!("source path: {:?}", path);

let mut from_paths = Vec::new();
from_paths.push(path);

let tmp_dir = tempfile::tempdir().unwrap();
println!("tmp_dir: {:?}", tmp_dir);
fs_extra::copy_items(&from_paths, &tmp_dir, &options).unwrap();

return tmp_dir;
}

}
18 changes: 17 additions & 1 deletion kondo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,26 @@ keywords = ["clean", "cleanup", "delete", "free"]
exclude = ["test_dir"]
edition = "2021"


[dependencies]
clap = { version = "4", features = ["derive"] }

# need recursive copy for testing
fs_extra = "1.3.0"

# need temporary test dirs
tempfile = "3.8.0"

[dependencies.kondo-lib]
path = "../kondo-lib"
version = "0.7"

[lib]
# rlib here is important for getting our integ test to run
# https://github.com/rust-lang/cargo/issues/6659
crate-type = ["rlib", "staticlib", "cdylib"]
bench = false

[[bin]]
name = "kondo"
test = true
bench = false
77 changes: 77 additions & 0 deletions kondo/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use std::{error::Error, fmt, num::ParseIntError};

#[derive(Debug)]
pub enum ParseAgeFilterError {
ParseIntError(ParseIntError),
InvalidUnit,
}

impl fmt::Display for ParseAgeFilterError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
ParseAgeFilterError::ParseIntError(e) => e.fmt(f),
ParseAgeFilterError::InvalidUnit => {
"invalid age unit, must be one of m, h, d, w, M, y".fmt(f)
}
}
}
}

impl From<ParseIntError> for ParseAgeFilterError {
fn from(e: ParseIntError) -> Self {
Self::ParseIntError(e)
}
}

impl Error for ParseAgeFilterError {}

pub fn parse_age_filter(age_filter: &str) -> Result<u64, ParseAgeFilterError> {
const MINUTE: u64 = 60;
const HOUR: u64 = MINUTE * 60;
const DAY: u64 = HOUR * 24;
const WEEK: u64 = DAY * 7;
const MONTH: u64 = WEEK * 4;
const YEAR: u64 = DAY * 365;

let (digit_end, unit) = age_filter
.char_indices()
.last()
.ok_or(ParseAgeFilterError::InvalidUnit)?;

let multiplier = match unit {
'm' => MINUTE,
'h' => HOUR,
'd' => DAY,
'w' => WEEK,
'M' => MONTH,
'y' => YEAR,
_ => return Err(ParseAgeFilterError::InvalidUnit),
};

let count = age_filter[..digit_end].parse::<u64>()?;
let seconds = count * multiplier;
Ok(seconds)
}

#[test]
fn test_age_filter_120s() {
let hours = parse_age_filter("2h").unwrap();
let minutes = parse_age_filter("120m").unwrap();

assert_eq!(minutes, hours);
}
#[test]
fn test_age_filter_10m() {
let res = parse_age_filter("10m");
let age_filter = res.unwrap();
assert_eq!(age_filter, (60 * 10));
}

#[ignore = "failing unexpectedly"]
#[test]
fn test_age_filter_year_months() {
let year = parse_age_filter("1y").unwrap();
let months = parse_age_filter("12M").unwrap();

assert_eq!(year, months);
}
Loading

0 comments on commit 92564ad

Please sign in to comment.