test: guard joinir strict env usage

This commit is contained in:
2025-12-30 07:42:52 +09:00
parent bb84612ac4
commit c87bdad59d
4 changed files with 89 additions and 55 deletions

View File

@ -199,6 +199,7 @@ fn is_var_named(node: &ASTNode, name: &str) -> bool {
mod tests {
use super::*;
use crate::ast::{BinaryOperator, Span};
use crate::tests::helpers::joinir_env::with_joinir_env_lock;
fn var(name: &str) -> ASTNode {
ASTNode::Variable {
@ -306,51 +307,53 @@ mod tests {
#[test]
fn strict_rejects_when_local_init_missing() {
// escape pattern exists, but `local ch = ...` is absent -> strict should reject
let body = vec![
ASTNode::If {
condition: Box::new(binop(BinaryOperator::Equal, var("ch"), str_lit("\""))),
then_body: vec![ASTNode::Break { span: Span::unknown() }],
else_body: None,
span: Span::unknown(),
},
ASTNode::If {
condition: Box::new(binop(BinaryOperator::Equal, var("ch"), str_lit("\\"))),
then_body: vec![
assignment(var("i"), binop(BinaryOperator::Add, var("i"), int_lit(1))),
assignment(
var("ch"),
method_call(
"s",
"substring",
vec![var("i"), binop(BinaryOperator::Add, var("i"), int_lit(1))],
with_joinir_env_lock(|| {
// escape pattern exists, but `local ch = ...` is absent -> strict should reject
let body = vec![
ASTNode::If {
condition: Box::new(binop(BinaryOperator::Equal, var("ch"), str_lit("\""))),
then_body: vec![ASTNode::Break { span: Span::unknown() }],
else_body: None,
span: Span::unknown(),
},
ASTNode::If {
condition: Box::new(binop(BinaryOperator::Equal, var("ch"), str_lit("\\"))),
then_body: vec![
assignment(var("i"), binop(BinaryOperator::Add, var("i"), int_lit(1))),
assignment(
var("ch"),
method_call(
"s",
"substring",
vec![var("i"), binop(BinaryOperator::Add, var("i"), int_lit(1))],
),
),
),
],
else_body: None,
span: Span::unknown(),
},
assignment(var("i"), binop(BinaryOperator::Add, var("i"), int_lit(1))),
];
],
else_body: None,
span: Span::unknown(),
},
assignment(var("i"), binop(BinaryOperator::Add, var("i"), int_lit(1))),
];
let prev = std::env::var("HAKO_JOINIR_STRICT").ok();
std::env::set_var("HAKO_JOINIR_STRICT", "1");
let decision = classify_p5b_escape_derived(&body, "i");
if let Some(v) = prev {
std::env::set_var("HAKO_JOINIR_STRICT", v);
} else {
std::env::remove_var("HAKO_JOINIR_STRICT");
}
match decision {
P5bEscapeDerivedDecision::Reject(reason) => {
assert!(
reason.contains("missing_local_init"),
"unexpected reason: {}",
reason
);
let prev = std::env::var("HAKO_JOINIR_STRICT").ok();
std::env::set_var("HAKO_JOINIR_STRICT", "1");
let decision = classify_p5b_escape_derived(&body, "i");
if let Some(v) = prev {
std::env::set_var("HAKO_JOINIR_STRICT", v);
} else {
std::env::remove_var("HAKO_JOINIR_STRICT");
}
other => panic!("expected Reject, got {:?}", other),
}
match decision {
P5bEscapeDerivedDecision::Reject(reason) => {
assert!(
reason.contains("missing_local_init"),
"unexpected reason: {}",
reason
);
}
other => panic!("expected Reject, got {:?}", other),
}
});
}
}

View File

@ -197,6 +197,7 @@ fn candidate_paths(base: &Path) -> Vec<PathBuf> {
mod tests {
use super::*;
use crate::config::nyash_toml_v2::{NyashConfigV2, PluginPaths};
use crate::tests::helpers::joinir_env::with_joinir_env_lock;
use std::env;
struct EnvGuard {
@ -228,6 +229,15 @@ mod tests {
}
}
fn ensure_ring0_initialized() {
use crate::runtime::ring0::{default_ring0, GLOBAL_RING0};
use std::sync::Arc;
if GLOBAL_RING0.get().is_none() {
let _ = GLOBAL_RING0.set(Arc::new(default_ring0()));
}
}
fn loader_with_missing_library(path: &str) -> PluginLoaderV2 {
let mut libraries = HashMap::new();
libraries.insert(
@ -250,22 +260,31 @@ mod tests {
#[test]
fn load_all_plugins_strict_fails_on_missing_library() {
let _guard = EnvGuard::set("HAKO_JOINIR_STRICT", "1");
let loader = loader_with_missing_library("/nonexistent/libnyash_filebox_plugin");
with_joinir_env_lock(|| {
ensure_ring0_initialized();
let _guard = EnvGuard::set("HAKO_JOINIR_STRICT", "1");
let loader = loader_with_missing_library("/nonexistent/libnyash_filebox_plugin");
let result = load_all_plugins(&loader);
assert!(result.is_err(), "strict mode must fail when library is missing");
let result = load_all_plugins(&loader);
assert!(
result.is_err(),
"strict mode must fail when library is missing"
);
});
}
#[test]
fn load_all_plugins_best_effort_continues_on_missing_library() {
let _guard = EnvGuard::unset("HAKO_JOINIR_STRICT");
let loader = loader_with_missing_library("/nonexistent/libnyash_filebox_plugin");
with_joinir_env_lock(|| {
ensure_ring0_initialized();
let _guard = EnvGuard::unset("HAKO_JOINIR_STRICT");
let loader = loader_with_missing_library("/nonexistent/libnyash_filebox_plugin");
let result = load_all_plugins(&loader);
assert!(
result.is_ok(),
"non-strict mode should continue even when a library is missing"
);
let result = load_all_plugins(&loader);
assert!(
result.is_ok(),
"non-strict mode should continue even when a library is missing"
);
});
}
}

View File

@ -4,6 +4,18 @@
//!
//! Note: JoinIR Core は常時 ON。`NYASH_JOINIR_CORE` は deprecated なので、セットは互換目的だけ。
#[cfg(test)]
use std::sync::Mutex;
#[cfg(test)]
static JOINIR_ENV_LOCK: Mutex<()> = Mutex::new(());
#[cfg(test)]
pub fn with_joinir_env_lock<F: FnOnce()>(f: F) {
let _guard = JOINIR_ENV_LOCK.lock().expect("joinir env lock poisoned");
f();
}
/// Core ON (joinir_core_enabled = true) にする。
#[allow(dead_code)]
pub fn set_core_on() {

View File

@ -1,4 +1,4 @@
mod helpers;
pub mod helpers;
#[cfg(feature = "aot-plan-import")]
pub mod aot_plan_import;