Skip to content
Merged
Show file tree
Hide file tree
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
51 changes: 31 additions & 20 deletions tests/features/manifest.feature
Original file line number Diff line number Diff line change
@@ -1,36 +1,47 @@
Feature: Manifest parsing

Scenario: Parse minimal manifest
When the manifest file "tests/data/minimal.yml" is parsed
Feature: Manifest Parsing
As a user,
I want to define my build in a YAML manifest,
So that Netsuke can understand and execute it.

Scenario: Parsing a minimal valid manifest
Given the manifest file "tests/data/minimal.yml" is parsed
When the version is checked
Then the manifest version is "1.0.0"
And the first target name is "hello"

Scenario: Parse phony and always flags
When the manifest file "tests/data/phony.yml" is parsed
Scenario: Parsing a manifest with phony and always flags
Given the manifest file "tests/data/phony.yml" is parsed
When the flags are checked
Then the first target is phony
And the first target is always rebuilt

Scenario: Actions are always treated as phony
When the manifest file "tests/data/actions.yml" is parsed
Scenario: A target in the 'actions' block is implicitly phony
Given the manifest file "tests/data/actions.yml" is parsed
When the flags are checked
Then the first action is phony

Scenario: Invalid action fails to parse
When the manifest file "tests/data/action_invalid.yml" is parsed
Then parsing the manifest fails

Scenario: Manifest with rules parses correctly
When the manifest file "tests/data/rules.yml" is parsed
Scenario: Parsing a manifest with rules
Given the manifest file "tests/data/rules.yml" is parsed
When the rules are checked
Then the first rule name is "compile"
And the first target name is "hello.o"

Scenario: Unknown field fails to parse
When the manifest file "tests/data/unknown_field.yml" is parsed
Scenario: Parsing fails for a manifest with an unknown top-level field
Given the manifest file "tests/data/unknown_field.yml" is parsed
When the parsing result is checked
Then parsing the manifest fails

Scenario: Parsing fails for a manifest with an invalid version string
Given the manifest file "tests/data/invalid_version.yml" is parsed
When the parsing result is checked
Then parsing the manifest fails

Scenario: Invalid version fails to parse
When the manifest file "tests/data/invalid_version.yml" is parsed
Scenario: Parsing fails for a target that is missing a recipe
Given the manifest file "tests/data/missing_recipe.yml" is parsed
When the parsing result is checked
Then parsing the manifest fails

Scenario: Missing recipe fails to parse
When the manifest file "tests/data/missing_recipe.yml" is parsed
Scenario: Parsing fails for an action that is missing a recipe
Given the manifest file "tests/data/action_invalid.yml" is parsed
When the parsing result is checked
Then parsing the manifest fails
59 changes: 39 additions & 20 deletions tests/steps/manifest_steps.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,15 @@
//! Step definitions for manifest parsing scenarios.
#![expect(
clippy::needless_pass_by_value,
reason = "Cucumber requires owned String arguments"
)]

use crate::CliWorld;
use cucumber::{then, when};
use cucumber::{given, then, when};
use netsuke::{ast::StringOrList, manifest};

#[expect(
clippy::needless_pass_by_value,
reason = "Cucumber requires owned String arguments"
)]
#[when(expr = "the manifest file {string} is parsed")]
fn parse_manifest(world: &mut CliWorld, path: String) {
match manifest::from_path(&path) {
fn parse_manifest_inner(world: &mut CliWorld, path: &str) {
match manifest::from_path(path) {
Ok(manifest) => {
world.manifest = Some(manifest);
world.manifest_error = None;
Expand All @@ -22,20 +21,44 @@ fn parse_manifest(world: &mut CliWorld, path: String) {
}
}

#[expect(
clippy::needless_pass_by_value,
reason = "Cucumber requires owned String arguments"
)]
fn assert_manifest(world: &CliWorld) {
assert!(
world.manifest.is_some(),
"manifest should have been parsed successfully"
);
}

fn assert_parsed(world: &CliWorld) {
assert!(
world.manifest.is_some() || world.manifest_error.is_some(),
"manifest should have been parsed"
);
}
#[given(expr = "the manifest file {string} is parsed")]
fn given_parse_manifest(world: &mut CliWorld, path: String) {
parse_manifest_inner(world, &path);
}

#[when(expr = "the manifest file {string} is parsed")]
fn parse_manifest(world: &mut CliWorld, path: String) {
parse_manifest_inner(world, &path);
}

#[when(regex = r"^the (?P<item>[a-z ]+) (?:is|are) checked$")]
fn when_item_checked(world: &mut CliWorld, item: String) {
match item.as_str() {
"parsing result" => assert_parsed(world),
"manifest" | "version" | "flags" | "rules" => assert_manifest(world),
unexpected => panic!("Unexpected item checked: '{unexpected}'"),
}
}

#[then(expr = "the manifest version is {string}")]
fn manifest_version(world: &mut CliWorld, version: String) {
let manifest = world.manifest.as_ref().expect("manifest");
assert_eq!(manifest.netsuke_version.to_string(), version);
}

#[expect(
clippy::needless_pass_by_value,
reason = "Cucumber requires owned String arguments"
)]
#[then(expr = "the first target name is {string}")]
fn first_target_name(world: &mut CliWorld, name: String) {
let manifest = world.manifest.as_ref().expect("manifest");
Expand Down Expand Up @@ -72,10 +95,6 @@ fn manifest_parse_error(world: &mut CliWorld) {
assert!(world.manifest_error.is_some(), "expected parse error");
}

#[expect(
clippy::needless_pass_by_value,
reason = "Cucumber requires owned String arguments"
)]
#[then(expr = "the first rule name is {string}")]
fn first_rule_name(world: &mut CliWorld, name: String) {
let manifest = world.manifest.as_ref().expect("manifest");
Expand Down