Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 85 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ struct ExtraToml {
/// The name of the hidden directory we store state inside.
const DOT_DIR: &str = ".haste";
/// The name of the haste config file.
const CONFIG_FILE: &str = "haste.toml";
const DEFAULT_CONFIG_FILE: &str = "haste.toml";

/// Uniquely identifies a benchmark.
#[derive(Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
Expand Down Expand Up @@ -140,17 +140,23 @@ impl ResultFile {
struct App {
/// The directory where persistent state is stored.
state_dir: PathBuf,
/// The path to the config file.
config_file: PathBuf,
}

impl App {
fn new() -> Self {
fn new(config_file: Option<PathBuf>) -> Self {
let state_dir = [env::current_dir().unwrap().to_str().unwrap(), DOT_DIR]
.iter()
.collect();
if !fs::exists(&state_dir).unwrap() {
fs::create_dir(&state_dir).unwrap();
}
Self { state_dir }
let config_file = config_file.unwrap_or_else(|| PathBuf::from(DEFAULT_CONFIG_FILE));
Self {
state_dir,
config_file,
}
}

/// Determine the next available datum ID.
Expand Down Expand Up @@ -222,14 +228,15 @@ impl App {
///
/// If successful, the new datum is printed to stdout.
fn cmd_bench(&self, comment: Option<String>) {
let config_text = fs::read_to_string(CONFIG_FILE).unwrap_or_else(|e| {
eprintln!("error: failed to read {CONFIG_FILE}: {e}");
let config_path = self.config_file.display();
let config_text = fs::read_to_string(&self.config_file).unwrap_or_else(|e| {
eprintln!("error: failed to read {config_path}: {e}");
process::exit(1);
});
let config: config::Config = match toml::from_str(&config_text) {
Ok(d) => d,
Err(e) => {
eprintln!("Unable to parse {CONFIG_FILE}: {e}");
eprintln!("Unable to parse {config_path}: {e}");
std::process::exit(1);
}
};
Expand Down Expand Up @@ -366,6 +373,10 @@ impl App {
#[derive(Parser)]
#[command(version, about, subcommand_required = true)]
struct Cli {
/// Path to the haste configuration file (defaults to haste.toml).
#[arg(short = 'f', long, global = true, value_name = "FILE")]
file: Option<PathBuf>,

#[command(subcommand)]
mode: Mode,
}
Expand All @@ -388,8 +399,8 @@ enum Mode {
}

fn main() {
let app = App::new();
let cli = Cli::parse();
let app = App::new(cli.file);
match cli.mode {
Mode::Bench { comment } => app.cmd_bench(comment),
Mode::Diff { id1, id2 } => app.cmd_diff(id1, id2),
Expand All @@ -399,7 +410,8 @@ fn main() {

#[cfg(test)]
mod tests {
use super::SummaryStats;
use super::{App, DEFAULT_CONFIG_FILE, SummaryStats};
use std::path::PathBuf;

#[test]
fn cis_overlap() {
Expand All @@ -426,4 +438,69 @@ mod tests {
assert!(s5.ci_overlaps(&s2));
assert!(!s5.ci_overlaps(&s3));
}

#[test]
fn test_default_config_path() {
use std::fs;

// Create a temporary directory for testing
let temp_dir = std::env::temp_dir().join("haste_test_default");
let _ = fs::remove_dir_all(&temp_dir);
fs::create_dir_all(&temp_dir).unwrap();

let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(&temp_dir).unwrap();

// Create the app with no config specified
let app = App::new(None);

// Should use the default CONFIG_FILE
assert_eq!(app.config_file, PathBuf::from(DEFAULT_CONFIG_FILE));

// Cleanup
std::env::set_current_dir(original_dir).unwrap();
let _ = fs::remove_dir_all(&temp_dir);
}

#[test]
fn test_config_file_loading() {
use std::fs;

let temp_dir = std::env::temp_dir().join("haste_test_loading");
let _ = fs::remove_dir_all(&temp_dir);
fs::create_dir_all(&temp_dir).unwrap();

// Create a test config file
let config_content = r#"
proc_execs = 2
inproc_iters = 3

[executors]
test_exec = "/bin/sh"

[suites.test_suite]
dir = "."
harness = "test.sh"

[suites.test_suite.benchmarks.test_bench]
extra_args = ["arg1"]
"#;
let config_path = temp_dir.join("test_config.toml");
fs::write(&config_path, config_content).unwrap();

// Parse the config
let config_text = fs::read_to_string(&config_path).unwrap();
let config: crate::config::Config = toml::from_str(&config_text).unwrap();

// Verify parsed values
assert_eq!(config.proc_execs, 2);
assert_eq!(config.inproc_iters, 3);
assert_eq!(config.executors.len(), 1);
assert!(config.executors.contains_key("test_exec"));
assert_eq!(config.suites.len(), 1);
assert!(config.suites.contains_key("test_suite"));

// Cleanup
let _ = fs::remove_dir_all(&temp_dir);
}
}