pub mod assertions;
use anyhow::Result;
use std::path::Path;
use tempfile::TempDir;
pub async fn create_test_repo() -> Result<TempDir> {
let temp_dir = TempDir::new()?;
std::process::Command::new("git")
.args(["init"])
.current_dir(temp_dir.path())
.output()?;
std::process::Command::new("git")
.args(["config", "user.email", "test@example.com"])
.current_dir(temp_dir.path())
.output()?;
std::process::Command::new("git")
.args(["config", "user.name", "Test User"])
.current_dir(temp_dir.path())
.output()?;
Ok(temp_dir)
}
pub fn create_file(dir: &Path, relative_path: &str, content: &str) -> Result<()> {
let file_path = dir.join(relative_path);
if let Some(parent) = file_path.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(file_path, content)?;
Ok(())
}
pub fn create_rust_project(dir: &Path) -> Result<()> {
std::fs::create_dir_all(dir.join("src"))?;
std::fs::create_dir_all(dir.join("tests"))?;
create_file(
dir,
"Cargo.toml",
r#"[package]
name = "test_project"
version = "0.1.0"
edition = "2021"
[dependencies]
anyhow = "1.0"
"#,
)?;
create_file(
dir,
"src/main.rs",
"fn main() {\n println!(\"Hello, world!\");\n}",
)?;
create_file(
dir,
"src/lib.rs",
"pub fn add(a: i32, b: i32) -> i32 {\n a + b\n}",
)?;
Ok(())
}
pub async fn run_command(command: &str, args: &[&str], dir: &Path) -> Result<String> {
let output = tokio::process::Command::new(command)
.args(args)
.current_dir(dir)
.output()
.await?;
if !output.status.success() {
let stderr = String::from_utf8_lossy(&output.stderr);
return Err(anyhow::anyhow!("Command failed: {}", stderr));
}
Ok(String::from_utf8_lossy(&output.stdout).to_string())
}
pub fn file_contains(path: &Path, content: &str) -> Result<bool> {
let file_content = std::fs::read_to_string(path)?;
Ok(file_content.contains(content))
}
pub fn count_files(dir: &Path) -> Result<usize> {
let mut count = 0;
for entry in walkdir::WalkDir::new(dir) {
let entry = entry?;
if entry.file_type().is_file() {
count += 1;
}
}
Ok(count)
}
pub fn dirs_equal(dir1: &Path, dir2: &Path) -> Result<bool> {
use std::collections::HashSet;
let files1: HashSet<_> = walkdir::WalkDir::new(dir1)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file())
.map(|e| e.path().strip_prefix(dir1).unwrap().to_path_buf())
.collect();
let files2: HashSet<_> = walkdir::WalkDir::new(dir2)
.into_iter()
.filter_map(|e| e.ok())
.filter(|e| e.file_type().is_file())
.map(|e| e.path().strip_prefix(dir2).unwrap().to_path_buf())
.collect();
if files1 != files2 {
return Ok(false);
}
for file in files1 {
let content1 = std::fs::read_to_string(dir1.join(&file))?;
let content2 = std::fs::read_to_string(dir2.join(&file))?;
if content1 != content2 {
return Ok(false);
}
}
Ok(true)
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_create_test_repo() {
let temp_dir = create_test_repo().await.unwrap();
assert!(temp_dir.path().join(".git").exists());
}
#[test]
fn test_create_rust_project() {
let temp_dir = TempDir::new().unwrap();
create_rust_project(temp_dir.path()).unwrap();
assert!(temp_dir.path().join("Cargo.toml").exists());
assert!(temp_dir.path().join("src/main.rs").exists());
assert!(temp_dir.path().join("src/lib.rs").exists());
}
#[test]
fn test_file_contains() {
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("test.txt");
std::fs::write(&file_path, "Hello, world!").unwrap();
assert!(file_contains(&file_path, "Hello").unwrap());
assert!(!file_contains(&file_path, "Goodbye").unwrap());
}
#[test]
fn test_dirs_equal_identical_dirs() {
let temp_dir = TempDir::new().unwrap();
let dir1 = temp_dir.path().join("dir1");
let dir2 = temp_dir.path().join("dir2");
std::fs::create_dir(&dir1).unwrap();
std::fs::create_dir(&dir2).unwrap();
std::fs::write(dir1.join("file1.txt"), "content1").unwrap();
std::fs::write(dir2.join("file1.txt"), "content1").unwrap();
std::fs::write(dir1.join("file2.txt"), "content2").unwrap();
std::fs::write(dir2.join("file2.txt"), "content2").unwrap();
assert!(dirs_equal(&dir1, &dir2).unwrap());
}
#[test]
fn test_dirs_equal_different_files() {
let temp_dir = TempDir::new().unwrap();
let dir1 = temp_dir.path().join("dir1");
let dir2 = temp_dir.path().join("dir2");
std::fs::create_dir(&dir1).unwrap();
std::fs::create_dir(&dir2).unwrap();
std::fs::write(dir1.join("file1.txt"), "content1").unwrap();
std::fs::write(dir2.join("file2.txt"), "content2").unwrap();
assert!(!dirs_equal(&dir1, &dir2).unwrap());
}
#[test]
fn test_dirs_equal_different_content() {
let temp_dir = TempDir::new().unwrap();
let dir1 = temp_dir.path().join("dir1");
let dir2 = temp_dir.path().join("dir2");
std::fs::create_dir(&dir1).unwrap();
std::fs::create_dir(&dir2).unwrap();
std::fs::write(dir1.join("file.txt"), "content1").unwrap();
std::fs::write(dir2.join("file.txt"), "content2").unwrap();
assert!(!dirs_equal(&dir1, &dir2).unwrap());
}
#[test]
fn test_dirs_equal_empty_dirs() {
let temp_dir = TempDir::new().unwrap();
let dir1 = temp_dir.path().join("dir1");
let dir2 = temp_dir.path().join("dir2");
std::fs::create_dir(&dir1).unwrap();
std::fs::create_dir(&dir2).unwrap();
assert!(dirs_equal(&dir1, &dir2).unwrap());
}
#[test]
fn test_dirs_equal_nested_structure() {
let temp_dir = TempDir::new().unwrap();
let dir1 = temp_dir.path().join("dir1");
let dir2 = temp_dir.path().join("dir2");
std::fs::create_dir(&dir1).unwrap();
std::fs::create_dir(&dir2).unwrap();
std::fs::create_dir(dir1.join("subdir")).unwrap();
std::fs::create_dir(dir2.join("subdir")).unwrap();
std::fs::write(dir1.join("subdir").join("nested.txt"), "nested content").unwrap();
std::fs::write(dir2.join("subdir").join("nested.txt"), "nested content").unwrap();
assert!(dirs_equal(&dir1, &dir2).unwrap());
}
#[test]
fn test_dirs_equal_missing_nested_file() {
let temp_dir = TempDir::new().unwrap();
let dir1 = temp_dir.path().join("dir1");
let dir2 = temp_dir.path().join("dir2");
std::fs::create_dir(&dir1).unwrap();
std::fs::create_dir(&dir2).unwrap();
std::fs::create_dir(dir1.join("subdir")).unwrap();
std::fs::create_dir(dir2.join("subdir")).unwrap();
std::fs::write(dir1.join("subdir").join("nested.txt"), "content").unwrap();
assert!(!dirs_equal(&dir1, &dir2).unwrap());
}
#[test]
fn test_dirs_equal_nonexistent_dir() {
let temp_dir = TempDir::new().unwrap();
let dir1 = temp_dir.path().join("dir1");
let dir2 = temp_dir.path().join("nonexistent");
std::fs::create_dir(&dir1).unwrap();
std::fs::write(dir1.join("file.txt"), "content").unwrap();
assert!(!dirs_equal(&dir1, &dir2).unwrap());
}
#[test]
fn test_dirs_equal_with_subdirs_only() {
let temp_dir = TempDir::new().unwrap();
let dir1 = temp_dir.path().join("dir1");
let dir2 = temp_dir.path().join("dir2");
std::fs::create_dir(&dir1).unwrap();
std::fs::create_dir(&dir2).unwrap();
std::fs::create_dir(dir1.join("empty_subdir")).unwrap();
std::fs::create_dir(dir2.join("empty_subdir")).unwrap();
assert!(dirs_equal(&dir1, &dir2).unwrap());
}
#[test]
fn test_count_files() {
let temp_dir = TempDir::new().unwrap();
let dir = temp_dir.path();
std::fs::write(dir.join("file1.txt"), "content1").unwrap();
std::fs::write(dir.join("file2.txt"), "content2").unwrap();
std::fs::create_dir(dir.join("subdir")).unwrap();
std::fs::write(dir.join("subdir").join("file3.txt"), "content3").unwrap();
assert_eq!(count_files(dir).unwrap(), 3);
}
}