Skip to content

Commit b4c30a7

Browse files
committed
Setup for test case builder CLI
1 parent 43c5c2b commit b4c30a7

File tree

10 files changed

+194
-59
lines changed

10 files changed

+194
-59
lines changed

src/common/src/types/test_cases_builder.rs

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,23 +3,61 @@ use std::collections::HashMap;
33

44
#[derive(Debug, Serialize, Deserialize, Clone)]
55
pub struct TestCasesBuilder {
6+
/// The name of the test case builder.
67
pub name: String,
8+
/// The description of the test case builder.
79
pub description: String,
10+
/// The set of steps to build the test cases.
811
pub set: Vec<SetSteps>,
12+
/// The labels to apply to the test cases.
913
pub labels: Option<Vec<String>>,
14+
/// The permutations to apply to the test cases.
1015
pub permutations: HashMap<String, Vec<String>>,
16+
/// The pub version of the test cases.
1117
pub version: u8,
1218
}
1319

20+
impl TestCasesBuilder {
21+
pub fn default() -> Self {
22+
let mut permutations = HashMap::new();
23+
permutations.insert(
24+
"Operating System".to_string(),
25+
vec![
26+
"Ubuntu 22.04".to_string(),
27+
"Windows 11".to_string(),
28+
"MacOS 12.0".to_string(),
29+
],
30+
);
31+
32+
TestCasesBuilder {
33+
name: "Demo test cases".to_string(),
34+
description: "description".to_string(),
35+
set: vec![SetSteps::Include(Filter {
36+
all_labels: None,
37+
any_names: None,
38+
negate: false,
39+
})],
40+
labels: Some(vec!["Demo".to_string()]),
41+
permutations,
42+
version: 1,
43+
}
44+
}
45+
}
46+
1447
#[derive(Debug, Serialize, Deserialize, Clone)]
1548
pub enum SetSteps {
49+
/// Include requirements that match the filter
1650
Include(Filter),
51+
/// Exclude requirements that match the filter
1752
Exclude(Filter),
1853
}
1954

2055
#[derive(Debug, Serialize, Deserialize, Clone)]
2156
pub struct Filter {
57+
/// The labels to filter the requirements.
2258
pub all_labels: Option<Vec<String>>,
59+
/// The names to filter the requirements.
2360
pub any_names: Option<Vec<String>>,
61+
/// If the filter should negate the result.
2462
pub negate: bool,
2563
}

src/yatm_v2/src/app/cli.rs

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
11
use crate::app::load_config::load_config;
2-
use crate::app::requirements_validate::get_requirements_from_files;
2+
use crate::app::requirements::get_requirements_from_files;
33
use crate::app::{
4-
init_config::init_config,
5-
requirements_validate::{
4+
init_workspace::init_workspace,
5+
requirements::{
66
get_requirements_from_file, validate_requirements_file, validate_requirements_files,
77
},
88
};
9-
use crate::types::RequirementsFile;
10-
use common::types::Requirement;
9+
use crate::types::{RequirementsFile, TestCasesBuilderFile};
1110

1211
use std::path::PathBuf;
1312

1413
use anyhow::{Context, Ok, Result};
1514
use clap::{Parser, Subcommand};
1615

17-
use super::requirements_validate::get_requirements_files;
16+
use super::requirements::get_requirements_files;
1817

1918
// Define the main application
2019
#[derive(Parser)]
@@ -27,9 +26,9 @@ struct App {
2726
// Define the top-level subcommands
2827
#[derive(Subcommand)]
2928
enum Commands {
30-
/// Create a directory to start working from
29+
/// Create a new YATM workspace
3130
Init {
32-
/// The path to the project
31+
/// The path to the new workspace
3332
#[clap(short, long)]
3433
path: PathBuf,
3534
},
@@ -91,6 +90,14 @@ struct Subcommand1Options {
9190

9291
#[derive(Subcommand)]
9392
enum TestCasesSubcommands {
93+
/// Create a new test case builder
94+
New {
95+
/// The path to the project
96+
#[clap(short, long, default_value = ".")]
97+
config_path: PathBuf,
98+
#[clap(short, long)]
99+
file_name: Option<String>,
100+
},
94101
/// Check the test cases
95102
Validate,
96103
/// List the test cases
@@ -113,8 +120,8 @@ pub fn cli() -> Result<()> {
113120
let app = App::parse();
114121
match app.command {
115122
Commands::Init { path } => {
116-
init_config(&path)?;
117-
println!("Created the project in {:?}", path);
123+
init_workspace(&path)?;
124+
println!("Created a YATM workspace in {:?}", path);
118125
}
119126
Commands::Requirements { subcommand } => match subcommand {
120127
RequirementsSubcommands::New {
@@ -133,10 +140,7 @@ pub fn cli() -> Result<()> {
133140
}
134141
};
135142

136-
if config.requirements_dirs.len() < 1 {
137-
anyhow::bail!("No requirements directories found");
138-
}
139-
let requirements_file_path = config.requirements_dirs[0].join(file_name);
143+
let requirements_file_path = config.new_requirements_dir.join(file_name);
140144
if requirements_file_path.exists() {
141145
anyhow::bail!(
142146
"The requirements file already exists: {:?}",
@@ -187,6 +191,44 @@ pub fn cli() -> Result<()> {
187191
}
188192
},
189193
Commands::TestCases { subcommand } => match subcommand {
194+
TestCasesSubcommands::New {
195+
config_path,
196+
file_name,
197+
} => {
198+
let config = load_config(&config_path)?;
199+
200+
let file_name = match file_name {
201+
Some(file_name) => file_name,
202+
None => {
203+
// get datetime string in current timezone
204+
let datetime_string =
205+
chrono::Local::now().format("%Y-%m-%d-%H-%M-%S").to_string();
206+
format!("test_cases_builder-{}.yaml", datetime_string)
207+
}
208+
};
209+
210+
let test_cases_builder_file_path =
211+
config.new_test_cases_builder_dir.join(file_name);
212+
if test_cases_builder_file_path.exists() {
213+
anyhow::bail!(
214+
"The test cases builder file already exists: {:?}",
215+
test_cases_builder_file_path
216+
);
217+
}
218+
let test_cases_builder_file = TestCasesBuilderFile::default();
219+
let test_cases_builder_file = serde_yaml::to_string(&test_cases_builder_file)
220+
.context("Failed to turn test cases builder file into a string")?;
221+
std::fs::write(&test_cases_builder_file_path, test_cases_builder_file).context(
222+
format!(
223+
"Failed to write the test cases builder file: {:?}",
224+
test_cases_builder_file_path
225+
),
226+
)?;
227+
println!(
228+
"Created the test cases builder file: {:?}",
229+
test_cases_builder_file_path
230+
);
231+
}
190232
TestCasesSubcommands::Validate => {
191233
println!("Running validate");
192234
}
@@ -220,6 +262,8 @@ mod test_cli {
220262
use predicates::prelude::predicate;
221263
use tempfile::tempdir;
222264

265+
use crate::app::load_config::load_config;
266+
223267
fn get_command() -> Command {
224268
Command::cargo_bin(env!("CARGO_PKG_NAME")).unwrap()
225269
}
@@ -249,15 +293,18 @@ mod test_cli {
249293
cmd.args(&["init", "--path", dir.to_str().unwrap()])
250294
.assert()
251295
.success()
252-
.stdout(predicate::str::contains("Created the project in"));
296+
.stdout(predicate::str::contains("Created a YATM workspace in"));
253297

254-
// check that files are generated correctly
298+
// load the config
255299
assert!(dir.join("config.yaml").is_file());
256-
assert!(dir.join(".generated_files").is_dir());
300+
let config = load_config(&dir).unwrap();
301+
302+
// check that files are generated correctly
303+
assert!(config.new_requirements_dir.is_dir());
304+
assert!(config.new_test_cases_builder_dir.is_dir());
305+
assert!(config.generated_files_dir.is_dir());
257306
assert!(dir.join(".gitignore").is_file());
258-
let requirements_dir = dir.join("requirements");
259-
assert!(requirements_dir.is_dir());
260-
assert_eq!(get_number_of_files_in_dir(&requirements_dir), 1);
307+
assert_eq!(get_number_of_files_in_dir(&config.new_requirements_dir), 1);
261308

262309
// run the requirements new command
263310
let new_requirements_file_name = "my-test-requirements.yaml";
@@ -272,9 +319,10 @@ mod test_cli {
272319
])
273320
.assert()
274321
.success();
275-
let new_requirements_file_path = requirements_dir.join(new_requirements_file_name);
322+
let new_requirements_file_path =
323+
config.new_requirements_dir.join(new_requirements_file_name);
276324
assert!(new_requirements_file_path.is_file());
277-
assert_eq!(get_number_of_files_in_dir(&requirements_dir), 2);
325+
assert_eq!(get_number_of_files_in_dir(&config.new_requirements_dir), 2);
278326

279327
// run the requirements new command without a file name
280328
let mut cmd = get_command();
@@ -286,7 +334,7 @@ mod test_cli {
286334
])
287335
.assert()
288336
.success();
289-
assert_eq!(get_number_of_files_in_dir(&requirements_dir), 3);
337+
assert_eq!(get_number_of_files_in_dir(&config.new_requirements_dir), 3);
290338

291339
// run the requirements list command
292340
let mut cmd = get_command();

src/yatm_v2/src/app/init_config.rs renamed to src/yatm_v2/src/app/init_workspace.rs

Lines changed: 40 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1-
use crate::types::Config;
1+
use crate::types::{Config, RequirementsFile, TestCasesBuilderFile};
22
use anyhow::{Context, Result};
33
use serde_yaml;
44
use std::path::PathBuf;
55

6-
pub fn init_config(dir: &PathBuf) -> Result<()> {
6+
/// Initialize the configuration directory.
7+
pub fn init_workspace(dir: &PathBuf) -> Result<()> {
78
make_sure_empty_dir_exists(dir)?;
89

10+
// Create the config file
911
let config = Config::default();
1012
let config_file = dir.join("config.yaml");
1113
std::fs::write(
@@ -14,24 +16,36 @@ pub fn init_config(dir: &PathBuf) -> Result<()> {
1416
)
1517
.context("Failed to write the config file")?;
1618

17-
if config.requirements_dirs.len() == 1 {
18-
let requirements_dir = dir.join(&config.requirements_dirs[0]);
19-
make_sure_empty_dir_exists(&requirements_dir)?;
20-
let datetime_string = chrono::Utc::now().format("%Y%m%d%H%M%S").to_string();
21-
let requirements_file_path =
22-
requirements_dir.join(format!("requirements-{}.yaml", datetime_string));
23-
let requirements_file = crate::types::RequirementsFile::default();
24-
std::fs::write(
25-
&requirements_file_path,
26-
serde_yaml::to_string(&requirements_file)
27-
.context("Failed to serialize the requirements")?,
28-
)?;
29-
}
19+
// Get the current datetime for naming files
20+
let datetime_string = chrono::Utc::now().format("%Y-%m-%d-%H-%M-%S").to_string();
21+
22+
// Create the requirements directory and file
23+
let requirements_dir = dir.join(&config.new_requirements_dir);
24+
make_sure_empty_dir_exists(&requirements_dir)?;
25+
let requirements_file_path =
26+
requirements_dir.join(format!("requirements-{}.yaml", datetime_string));
27+
let requirements_file = RequirementsFile::default();
28+
std::fs::write(
29+
&requirements_file_path,
30+
serde_yaml::to_string(&requirements_file)
31+
.context("Failed to serialize the requirements")?,
32+
)?;
33+
34+
// Create the test cases builder directory and file
35+
let test_cases_builder_dir = dir.join(&config.new_test_cases_builder_dir);
36+
make_sure_empty_dir_exists(&test_cases_builder_dir)?;
37+
let test_cases_builder_file =
38+
test_cases_builder_dir.join(format!("test_cases_builder-{}.yaml", datetime_string));
39+
std::fs::write(
40+
&test_cases_builder_file,
41+
serde_yaml::to_string(&TestCasesBuilderFile::default())
42+
.context("Failed to serialize the test cases builder")?,
43+
)
44+
.context("Failed to write the test cases builder file")?;
3045

46+
// Create the generated files directory and .gitignore file
3147
let generated_files_dir = dir.join(&config.generated_files_dir);
3248
make_sure_empty_dir_exists(&generated_files_dir)?;
33-
34-
// add a .gitignore file with the generated_files_dir
3549
let gitignore_file = dir.join(".gitignore");
3650
std::fs::write(
3751
&gitignore_file,
@@ -44,41 +58,37 @@ pub fn init_config(dir: &PathBuf) -> Result<()> {
4458

4559
#[cfg(test)]
4660
mod test_init_config {
47-
use super::init_config;
61+
use crate::app::load_config::load_config;
62+
63+
use super::init_workspace;
4864
use std::fs;
4965
use tempfile::tempdir;
5066

5167
#[test]
5268
fn init_config_creates_files() {
5369
let dir = tempdir().unwrap().path().to_path_buf();
54-
init_config(&dir).unwrap();
70+
init_workspace(&dir).unwrap();
5571
assert!(dir.join("config.yaml").is_file());
5672

57-
let requirements_dir = dir.join("requirements");
58-
assert!(requirements_dir.is_dir());
59-
60-
// check the requirements file is created
61-
let mut entries = fs::read_dir(&requirements_dir).unwrap();
62-
assert!(entries.next().is_some());
63-
assert!(entries.next().is_none());
73+
let config = load_config(&dir).unwrap();
6474

65-
assert!(dir.join(".generated_files").is_dir());
66-
assert!(dir.join(".gitignore").is_file());
75+
assert!(config.new_requirements_dir.is_dir());
76+
assert!(config.new_test_cases_builder_dir.is_dir());
6777
}
6878

6979
#[test]
7080
fn init_config_exists_not_empty() {
7181
let dir = tempdir().unwrap().path().to_path_buf();
7282
fs::create_dir(&dir).unwrap();
7383
fs::File::create(dir.join("file")).unwrap();
74-
assert!(init_config(&dir).is_err());
84+
assert!(init_workspace(&dir).is_err());
7585
}
7686

7787
#[test]
7888
fn init_config_exists_file() {
7989
let dir = tempdir().unwrap().path().to_path_buf();
8090
fs::File::create(&dir).unwrap();
81-
assert!(init_config(&dir).is_err());
91+
assert!(init_workspace(&dir).is_err());
8292
}
8393
}
8494

src/yatm_v2/src/app/load_config.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use anyhow::{Context, Result};
33
use serde_yaml;
44
use std::path::PathBuf;
55

6+
/// Load the configuration.
67
pub fn load_config(path: &PathBuf) -> Result<Config> {
78
let mut path = path.clone();
89
if path.is_dir() {
@@ -14,15 +15,23 @@ pub fn load_config(path: &PathBuf) -> Result<Config> {
1415
let mut config = serde_yaml::from_str::<Config>(&config)
1516
.context(format!("Failed to deserialize the config: {:?}", path))?;
1617

18+
// Update the paths to be relative to the config file
1719
let parent_dir = path
1820
.parent()
1921
.context(format!("Failed to get the parent directory: {:?}", path))?;
20-
config.generated_files_dir = parent_dir.join(config.generated_files_dir);
2122
config.requirements_dirs = config
2223
.requirements_dirs
2324
.iter()
2425
.map(|dir| parent_dir.join(dir))
2526
.collect();
27+
config.new_requirements_dir = parent_dir.join(config.new_requirements_dir);
28+
config.test_cases_builders_dirs = config
29+
.test_cases_builders_dirs
30+
.iter()
31+
.map(|file| parent_dir.join(file))
32+
.collect();
33+
config.new_test_cases_builder_dir = parent_dir.join(config.new_test_cases_builder_dir);
34+
config.generated_files_dir = parent_dir.join(config.generated_files_dir);
2635

2736
Ok(config)
2837
}

src/yatm_v2/src/app/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
mod cli;
2-
mod init_config;
2+
mod init_workspace;
33
mod load_config;
4-
mod requirements_validate;
4+
mod requirements;
55

66
pub use cli::cli;

0 commit comments

Comments
 (0)