use rstest::fixture;
use rstest_bdd_macros::{given, scenario, then, when};
use std::cell::RefCell;
use whitaker_common::lcom4::{MethodInfo, MethodInfoBuilder};
#[derive(Debug, Default)]
struct ExtractionWorld {
builder: RefCell<Option<MethodInfoBuilder>>,
result: RefCell<Option<MethodInfo>>,
}
impl ExtractionWorld {
fn with_builder(&self, f: impl FnOnce(&mut MethodInfoBuilder)) {
let mut slot = self.builder.borrow_mut();
if let Some(builder) = slot.as_mut() {
f(builder);
}
}
fn build(&self) {
if let Some(builder) = self.builder.borrow_mut().take() {
self.result.replace(Some(builder.build()));
}
}
#[expect(
clippy::expect_used,
reason = "build result is required for this behaviour test"
)]
fn with_result<T>(&self, f: impl FnOnce(&MethodInfo) -> T) -> T {
let borrow = self.result.borrow();
let info = borrow
.as_ref()
.expect("result is None — did you forget the When step?");
f(info)
}
fn has_field(&self, field: &str) -> bool {
self.with_result(|info| info.accessed_fields().contains(field))
}
fn has_call(&self, method: &str) -> bool {
self.with_result(|info| info.called_methods().contains(method))
}
fn fields_empty(&self) -> bool {
self.with_result(|info| info.accessed_fields().is_empty())
}
fn calls_empty(&self) -> bool {
self.with_result(|info| info.called_methods().is_empty())
}
}
#[fixture]
fn world() -> ExtractionWorld {
ExtractionWorld::default()
}
#[given("an extraction builder for method {name}")]
fn given_builder(world: &ExtractionWorld, name: String) {
world.builder.replace(Some(MethodInfoBuilder::new(name)));
}
#[given("a field access to {field} not from expansion")]
fn given_field_not_expanded(world: &ExtractionWorld, field: String) {
world.with_builder(|b| b.record_field_access(&field, false));
}
#[given("a field access to {field} from expansion")]
fn given_field_expanded(world: &ExtractionWorld, field: String) {
world.with_builder(|b| b.record_field_access(&field, true));
}
#[given("a method call to {method} not from expansion")]
fn given_call_not_expanded(world: &ExtractionWorld, method: String) {
world.with_builder(|b| b.record_method_call(&method, false));
}
#[given("a method call to {method} from expansion")]
fn given_call_expanded(world: &ExtractionWorld, method: String) {
world.with_builder(|b| b.record_method_call(&method, true));
}
#[when("the method info is built")]
fn when_build(world: &ExtractionWorld) {
world.build();
}
#[then("the accessed fields contain {field}")]
fn then_fields_contain(world: &ExtractionWorld, field: String) {
assert!(
world.has_field(&field),
"expected accessed_fields to contain '{field}'"
);
}
#[then("the accessed fields do not contain {field}")]
fn then_fields_not_contain(world: &ExtractionWorld, field: String) {
assert!(
!world.has_field(&field),
"expected accessed_fields NOT to contain '{field}'"
);
}
#[then("the called methods contain {method}")]
fn then_calls_contain(world: &ExtractionWorld, method: String) {
assert!(
world.has_call(&method),
"expected called_methods to contain '{method}'"
);
}
#[then("the called methods do not contain {method}")]
fn then_calls_not_contain(world: &ExtractionWorld, method: String) {
assert!(
!world.has_call(&method),
"expected called_methods NOT to contain '{method}'"
);
}
#[then("the accessed fields are empty")]
fn then_fields_empty(world: &ExtractionWorld) {
assert!(world.fields_empty(), "expected accessed_fields to be empty");
}
#[then("the called methods are empty")]
fn then_calls_empty(world: &ExtractionWorld) {
assert!(world.calls_empty(), "expected called_methods to be empty");
}
#[scenario(path = "tests/features/method_extraction.feature", index = 0)]
fn scenario_field_access_recorded(world: ExtractionWorld) {
let _ = world;
}
#[scenario(path = "tests/features/method_extraction.feature", index = 1)]
fn scenario_method_call_recorded(world: ExtractionWorld) {
let _ = world;
}
#[scenario(path = "tests/features/method_extraction.feature", index = 2)]
fn scenario_macro_field_filtered(world: ExtractionWorld) {
let _ = world;
}
#[scenario(path = "tests/features/method_extraction.feature", index = 3)]
fn scenario_macro_call_filtered(world: ExtractionWorld) {
let _ = world;
}
#[scenario(path = "tests/features/method_extraction.feature", index = 4)]
fn scenario_all_expansion_empty(world: ExtractionWorld) {
let _ = world;
}
#[scenario(path = "tests/features/method_extraction.feature", index = 5)]
fn scenario_empty_builder(world: ExtractionWorld) {
let _ = world;
}
#[scenario(path = "tests/features/method_extraction.feature", index = 6)]
fn scenario_multiple_accumulate(world: ExtractionWorld) {
let _ = world;
}