Unit tests for basic Rune registration
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Deukhoofd 2024-05-18 19:24:47 +02:00
parent 42bee5e37c
commit 535f6bf79b
Signed by: Deukhoofd
GPG Key ID: F63E044490819F6F
3 changed files with 512 additions and 94 deletions

View File

@ -1,6 +1,8 @@
use crate::dynamic_data::ExecutingMove; use crate::dynamic_data::{ExecutingMove, HitData};
use crate::script_implementations::rune::wrappers::dynamic_data::pokemon::RunePokemon;
use crate::script_implementations::rune::wrappers::dynamic_data::resolve_script_data;
use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneWrapper}; use crate::script_implementations::rune::wrappers::{impl_rune_wrapper, RuneWrapper};
use rune::runtime::{AnyObj, Shared}; use rune::runtime::{AnyObj, Object, Shared};
use rune::Any; use rune::Any;
use std::sync::Arc; use std::sync::Arc;
@ -9,6 +11,9 @@ pub fn register(module: &mut rune::Module) -> anyhow::Result<()> {
module.function_meta(RuneExecutingMove::target_count)?; module.function_meta(RuneExecutingMove::target_count)?;
module.function_meta(RuneExecutingMove::number_of_hits)?; module.function_meta(RuneExecutingMove::number_of_hits)?;
module.function_meta(RuneExecutingMove::user)?; module.function_meta(RuneExecutingMove::user)?;
module.function_meta(RuneExecutingMove::chosen_move)?;
module.function_meta(RuneExecutingMove::use_move)?;
module.function_meta(RuneExecutingMove::script)?;
Ok(()) Ok(())
} }
@ -25,4 +30,43 @@ impl RuneExecutingMove {
fn number_of_hits(&self) -> u8 { self.0.number_of_hits() } fn number_of_hits(&self) -> u8 { self.0.number_of_hits() }
#[rune::function] #[rune::function]
fn user(&self) -> Shared<AnyObj> { self.0.user().wrap() } fn user(&self) -> Shared<AnyObj> { self.0.user().wrap() }
#[rune::function]
fn chosen_move(&self) -> Shared<AnyObj> { self.0.chosen_move().wrap() }
#[rune::function]
fn use_move(&self) -> Shared<AnyObj> { self.0.use_move().wrap() }
#[rune::function]
fn script(&self) -> Option<Shared<Object>> { resolve_script_data(self.0.script()) }
#[rune::function]
fn get_hit_data(&self, for_target: RunePokemon, hit: u8) -> anyhow::Result<Shared<AnyObj>> {
self.0.get_hit_data(&for_target.0, hit).map(|hit_data| hit_data.wrap())
}
}
#[derive(Debug, Clone, Any)]
pub struct RuneHitData(pub Arc<HitData>);
impl_rune_wrapper!(&Arc<HitData>, RuneHitData);
impl RuneHitData {
#[rune::function]
fn is_critical(&self) -> bool { self.0.is_critical() }
#[rune::function]
fn base_power(&self) -> u8 { self.0.base_power() }
#[rune::function]
fn effectiveness(&self) -> f32 { self.0.effectiveness() }
#[rune::function]
fn damage(&self) -> u32 { self.0.damage() }
#[rune::function]
fn move_type(&self) -> u8 { u8::from(self.0.move_type()) }
#[rune::function]
fn fail(&self) { self.0.fail() }
} }

View File

@ -1,3 +1,4 @@
use rune::alloc::fmt::TryWrite;
use rune::runtime::{AnyObj, Protocol, Shared, VmResult}; use rune::runtime::{AnyObj, Protocol, Shared, VmResult};
use rune::{Any, Value}; use rune::{Any, Value};
use std::num::Saturating; use std::num::Saturating;
@ -21,16 +22,17 @@ pub fn module() -> anyhow::Result<rune::Module> {
module.associated_function(Protocol::MUL_ASSIGN, RuneValueIntWrapper::mul_assign)?; module.associated_function(Protocol::MUL_ASSIGN, RuneValueIntWrapper::mul_assign)?;
module.associated_function(Protocol::PARTIAL_EQ, RuneValueIntWrapper::partial_eq)?; module.associated_function(Protocol::PARTIAL_EQ, RuneValueIntWrapper::partial_eq)?;
module.associated_function(Protocol::EQ, RuneValueIntWrapper::eq)?; module.associated_function(Protocol::EQ, RuneValueIntWrapper::eq)?;
module.associated_function(Protocol::STRING_DISPLAY, RuneValueIntWrapper::string_display)?; module.function_meta(RuneValueIntWrapper::string_display)?;
module.ty::<RuneValueBoolWrapper>()?; module.ty::<RuneValueBoolWrapper>()?;
module.function_meta(RuneValueBoolWrapper::rn_as_bool)?; module.function_meta(RuneValueBoolWrapper::rn_as_bool)?;
module.function_meta(RuneValueBoolWrapper::set_value)?; module.function_meta(RuneValueBoolWrapper::set_value)?;
module.associated_function(Protocol::PARTIAL_EQ, RuneValueBoolWrapper::eq)?;
module.associated_function(Protocol::EQ, RuneValueBoolWrapper::eq)?; module.associated_function(Protocol::EQ, RuneValueBoolWrapper::eq)?;
module.associated_function(Protocol::STRING_DISPLAY, RuneValueBoolWrapper::string_display)?; module.function_meta(RuneValueBoolWrapper::string_display)?;
module.ty::<RuneStringKey>()?; module.ty::<RuneStringKey>()?;
module.associated_function(Protocol::STRING_DISPLAY, RuneStringKey::string_display)?; module.function_meta(RuneStringKey::string_display)?;
parameters::register(&mut module)?; parameters::register(&mut module)?;
dynamic_data::register(&mut module)?; dynamic_data::register(&mut module)?;
@ -103,36 +105,76 @@ impl RuneValueIntWrapper {
fn sub_assign(&mut self, other: i64) { self.value -= other; } fn sub_assign(&mut self, other: i64) { self.value -= other; }
fn div_assign(&mut self, other: i64) { self.value /= other; } fn div_assign(&mut self, other: Value) {
match other {
Value::Integer(other) => self.value /= other,
Value::Float(other) => self.div_assign_f64(other),
_ => (),
}
}
fn mul_assign(&mut self, other: i64) { self.value *= other; } fn div_assign_f64(&mut self, other: f64) {
let v = (self.value.0 as f64 / other).floor();
if v > i64::MAX as f64 {
self.value = Saturating(i64::MAX);
} else if v < i64::MIN as f64 {
self.value = Saturating(i64::MIN);
} else {
self.value = Saturating(v as i64);
}
}
fn mul_assign(&mut self, other: Value) {
match other {
Value::Integer(other) => self.value *= other,
Value::Float(other) => self.mul_assign_f64(other),
_ => (),
}
}
fn mul_assign_f64(&mut self, other: f64) {
let v = (self.value.0 as f64 * other).floor();
if v > i64::MAX as f64 {
self.value = Saturating(i64::MAX);
} else if v < i64::MIN as f64 {
self.value = Saturating(i64::MIN);
} else {
self.value = Saturating(v as i64);
}
}
fn partial_eq(&self, b: i64) -> VmResult<bool> { VmResult::Ok(self.value.0 == b) } fn partial_eq(&self, b: i64) -> VmResult<bool> { VmResult::Ok(self.value.0 == b) }
fn eq(&self, b: i64) -> VmResult<bool> { VmResult::Ok(self.value.0 == b) } fn eq(&self, b: i64) -> VmResult<bool> { VmResult::Ok(self.value.0 == b) }
fn string_display(&self) -> VmResult<String> { VmResult::Ok(format!("{}", self.value.0)) } #[rune::function(instance, protocol = STRING_DISPLAY)]
fn string_display(&self, f: &mut rune::runtime::Formatter) -> VmResult<()> {
rune::vm_write!(f, "{}", self.value.0);
VmResult::Ok(())
}
} }
#[derive(Any, Clone)] #[derive(Any, Clone)]
struct RuneValueBoolWrapper { struct RuneValueBoolWrapper(bool);
value: bool,
}
impl RuneValueBoolWrapper { impl RuneValueBoolWrapper {
pub fn new(value: bool) -> Self { Self { value } } pub fn new(value: bool) -> Self { Self(value) }
pub fn as_bool(&self) -> bool { self.value } pub fn as_bool(&self) -> bool { self.0 }
#[rune::function(path = RuneValueIntWrapper::as_bool)] #[rune::function(path = RuneValueIntWrapper::as_bool)]
fn rn_as_bool(&self) -> bool { self.value } fn rn_as_bool(&self) -> bool { self.0 }
#[rune::function] #[rune::function]
fn set_value(&mut self, value: bool) { self.value = value; } fn set_value(&mut self, value: bool) { self.0 = value; }
fn string_display(&self) -> VmResult<String> { VmResult::Ok(format!("{}", self.value)) } #[rune::function(instance, protocol = STRING_DISPLAY)]
fn string_display(&self, f: &mut rune::runtime::Formatter) -> VmResult<()> {
rune::vm_write!(f, "{}", self.0);
VmResult::Ok(())
}
fn eq(&self, b: bool) -> VmResult<bool> { VmResult::Ok(self.value == b) } fn eq(&self, b: bool) -> VmResult<bool> { VmResult::Ok(self.0 == b) }
} }
#[derive(Any, Clone, Debug)] #[derive(Any, Clone, Debug)]
@ -141,9 +183,416 @@ pub(super) struct RuneStringKey(pub StringKey);
impl RuneStringKey { impl RuneStringKey {
pub fn new(value: StringKey) -> Self { Self(value) } pub fn new(value: StringKey) -> Self { Self(value) }
fn string_display(&self) -> VmResult<String> { VmResult::Ok(format!("{}", self.0)) } #[rune::function(instance, protocol = STRING_DISPLAY)]
fn string_display(&self, f: &mut rune::runtime::Formatter) -> VmResult<()> {
rune::vm_write!(f, "{}", self.0);
VmResult::Ok(())
}
} }
impl RuneWrapper for &StringKey { impl RuneWrapper for &StringKey {
fn wrap(self) -> Shared<AnyObj> { Shared::new(AnyObj::new(RuneStringKey(self.clone())).unwrap()).unwrap() } fn wrap(self) -> Shared<AnyObj> { Shared::new(AnyObj::new(RuneStringKey(self.clone())).unwrap()).unwrap() }
} }
#[cfg(test)]
mod test {
use super::*;
use rune::{Context, Diagnostics, Options, Source, Vm};
use std::sync::Arc;
pub fn setup_script(script: &str) -> Vm {
let mut context = Context::with_default_modules().unwrap();
context.install(module().unwrap()).unwrap();
let mut sources = rune::Sources::new();
sources.insert(Source::memory(script).unwrap()).unwrap();
let mut diagnostics = Diagnostics::new();
let mut options = Options::default();
options.debug_info(true);
options.memoize_instance_fn(true);
options.macros(true);
options.bytecode(true);
let unit = rune::prepare(&mut sources)
.with_context(&context)
.with_diagnostics(&mut diagnostics)
.with_options(&options)
.build()
.unwrap();
if !diagnostics.is_empty() {
panic!("Diagnostics: {:?}", diagnostics);
}
Vm::new(Arc::new(context.runtime().unwrap()), Arc::new(unit))
}
macro_rules! execute_vm {
($vm:expr, $func:expr, $val:expr) => {{
let args = vec![$val.clone()];
$vm.execute([$func], args).unwrap().complete().into_result().unwrap()
}};
}
#[test]
fn test_int_wrapper_set_value() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a.set_value(5);
}
"#,
);
let val = wrap_int_reference(10).unwrap();
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val).unwrap();
assert_eq!(v, 5);
}
#[test]
fn test_int_wrapper_as_int() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a.as_int()
}
"#,
);
let val = wrap_int_reference(10).unwrap();
let res = execute_vm!(vm, "test_int", val);
let res = res.as_integer().unwrap();
assert_eq!(res, 10);
}
#[test]
fn test_int_wrapper_add() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a += 5;
}
"#,
);
let val = wrap_int_reference(10).unwrap();
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val).unwrap();
assert_eq!(v, 15);
}
#[test]
fn test_int_wrapper_add_overflow() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a += 5;
}
"#,
);
let val = wrap_int_reference(i64::MAX).unwrap();
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val).unwrap();
assert_eq!(v, i64::MAX);
}
#[test]
fn test_int_wrapper_sub() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a -= 5;
}
"#,
);
let val = wrap_int_reference(10).unwrap();
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val).unwrap();
assert_eq!(v, 5);
}
#[test]
fn test_int_wrapper_underflow() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a -= 5;
}
"#,
);
let val = wrap_int_reference(i64::MIN).unwrap();
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val).unwrap();
assert_eq!(v, i64::MIN);
}
#[test]
fn test_int_wrapper_mul() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a *= 5;
}
"#,
);
let val = wrap_int_reference(10).unwrap();
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val).unwrap();
assert_eq!(v, 50);
}
#[test]
fn test_int_wrapper_mul_float() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a *= 0.5;
}
"#,
);
let val = wrap_int_reference(10).unwrap();
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val).unwrap();
assert_eq!(v, 5);
}
#[test]
fn test_int_wrapper_mul_overflow() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a *= 5;
}
"#,
);
let val = wrap_int_reference(i64::MAX).unwrap();
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val).unwrap();
assert_eq!(v, i64::MAX);
}
#[test]
fn test_int_wrapper_mul_float_overflow() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a *= 10.0;
}
"#,
);
let val = wrap_int_reference(i64::MAX).unwrap();
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val).unwrap();
assert_eq!(v, i64::MAX);
}
#[test]
fn test_int_wrapper_mul_float_underflow() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a *= 10.0;
}
"#,
);
let val = wrap_int_reference(i64::MIN).unwrap();
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val).unwrap();
assert_eq!(v, i64::MIN);
}
#[test]
fn test_int_wrapper_div() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a /= 5;
}
"#,
);
let val = wrap_int_reference(10).unwrap();
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val).unwrap();
assert_eq!(v, 2);
}
#[test]
fn test_int_wrapper_div_float() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a /= 0.5;
}
"#,
);
let val = wrap_int_reference(10).unwrap();
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val).unwrap();
assert_eq!(v, 20);
}
#[test]
fn test_int_wrapper_div_float_overflow() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a /= 0.0001;
}
"#,
);
let val = wrap_int_reference(i64::MAX).unwrap();
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val).unwrap();
assert_eq!(v, i64::MAX);
}
#[test]
fn test_int_wrapper_div_float_underflow() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a /= 0.0001;
}
"#,
);
let val = wrap_int_reference(i64::MIN).unwrap();
execute_vm!(vm, "test_int", val);
let v = get_int_reference_value(val).unwrap();
assert_eq!(v, i64::MIN);
}
#[test]
fn test_int_wrapper_eq() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a == 5
}
"#,
);
let val = wrap_int_reference(5).unwrap();
let res = execute_vm!(vm, "test_int", val);
let res = res.as_bool().unwrap();
assert_eq!(res, true);
}
#[test]
fn test_int_wrapper_ineq() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
a == 5
}
"#,
);
let val = wrap_int_reference(6).unwrap();
let res = execute_vm!(vm, "test_int", val);
let res = res.as_bool().unwrap();
assert_eq!(res, false);
}
#[test]
fn test_int_wrapper_string_display() {
let mut vm = setup_script(
r#"
pub fn test_int(a) {
`${a}`
}
"#,
);
let val = wrap_int_reference(5).unwrap();
let res = execute_vm!(vm, "test_int", val);
let res = res.into_string().unwrap().take().unwrap().into_std();
assert_eq!(res, "5");
}
#[test]
fn test_bool_wrapper_set_value() {
let mut vm = setup_script(
r#"
pub fn test_bool(a) {
a.set_value(true);
}
"#,
);
let val = wrap_bool_reference(false).unwrap();
execute_vm!(vm, "test_bool", val);
let v = get_bool_reference_value(val).unwrap();
assert_eq!(v, true);
}
#[test]
fn test_bool_wrapper_as_bool() {
let mut vm = setup_script(
r#"
pub fn test_bool(a) {
a.as_bool()
}
"#,
);
let val = wrap_bool_reference(true).unwrap();
let res = execute_vm!(vm, "test_bool", val);
let res = res.as_bool().unwrap();
assert_eq!(res, true);
}
#[test]
fn test_bool_wrapper_eq() {
let mut vm = setup_script(
r#"
pub fn test_bool(a) {
a == true
}
"#,
);
let val = wrap_bool_reference(true).unwrap();
let res = execute_vm!(vm, "test_bool", val);
let res = res.as_bool().unwrap();
assert_eq!(res, true);
}
#[test]
fn test_bool_wrapper_ineq() {
let mut vm = setup_script(
r#"
pub fn test_bool(a) {
a == true
}
"#,
);
let val = wrap_bool_reference(false).unwrap();
let res = execute_vm!(vm, "test_bool", val);
let res = res.as_bool().unwrap();
assert_eq!(res, false);
}
#[test]
fn test_bool_wrapper_string_display() {
let mut vm = setup_script(
r#"
pub fn test_bool(a) {
`${a}`
}
"#,
);
let val = wrap_bool_reference(true).unwrap();
let res = execute_vm!(vm, "test_bool", val);
let res = res.into_string().unwrap().take().unwrap().into_std();
assert_eq!(res, "true");
}
#[test]
fn test_string_key_wrapper_string_display() {
let mut vm = setup_script(
r#"
pub fn test_string_key(a) {
`${a}`
}
"#,
);
let val = RuneStringKey::new(StringKey::from("test"));
let val = Shared::new(AnyObj::new(val).unwrap()).unwrap();
let arg = Value::Any(val);
let res = execute_vm!(vm, "test_string_key", arg);
let res = res.into_string().unwrap().take().unwrap().into_std();
assert_eq!(res, "test");
}
}

View File

@ -5,10 +5,7 @@
use std::sync::{Arc, LazyLock}; use std::sync::{Arc, LazyLock};
use pkmn_lib::dynamic_data::{ use pkmn_lib::dynamic_data::{DynamicLibrary, PassChoice, PokemonBuilder, ScriptCategory, ScriptOwnerData, TurnChoice};
Battle, BattleParty, DamageSource, DynamicLibrary, ExecutingMove, MoveChoice, PassChoice, PokemonBuilder,
PokemonParty, ScriptCategory, ScriptContainer, ScriptOwnerData, TurnChoice, VolatileScriptsOwner,
};
use crate::common::library_loader; use crate::common::library_loader;
@ -106,75 +103,3 @@ fn load_non_existing_wasm_script() {
assert!(script.is_none()); assert!(script.is_none());
} }
/// Assurance has the interesting properties that it creates a special data script internally, and
/// deletes that data script through the get_owner functionality.
#[test]
fn validate_assurance() {
let lib = get_library();
let p1 = PokemonBuilder::new(lib.clone(), "charizard".into(), 100)
.learn_move("assurance".into())
.build()
.unwrap();
let p2 = PokemonBuilder::new(lib.clone(), "venusaur".into(), 100)
.build()
.unwrap();
let party1 = Arc::new(
BattleParty::new(
Arc::new(PokemonParty::new_from_vec(vec![Some(p1.clone())])),
vec![(0, 0)],
)
.unwrap(),
);
let party2 = Arc::new(
BattleParty::new(
Arc::new(PokemonParty::new_from_vec(vec![Some(p2.clone())])),
vec![(1, 0)],
)
.unwrap(),
);
let battle = Battle::new(lib.clone(), vec![party1, party2], false, 2, 1, None);
battle.sides()[0].set_pokemon(0, Some(p1.clone())).unwrap();
battle.sides()[1].set_pokemon(0, Some(p2.clone())).unwrap();
let script = lib
.load_script(
ScriptOwnerData::None,
ScriptCategory::Move,
&"double_power_if_target_damaged_in_turn".into(),
)
.unwrap()
.unwrap();
let mv = p1.learned_moves().read()[0].as_ref().unwrap().clone();
let choice = Arc::new(TurnChoice::Move(MoveChoice::new(p1.clone(), mv.clone(), 1, 0)));
script.on_before_turn(&choice).unwrap();
assert!(battle.sides()[1].has_volatile_script(&"double_power_if_target_damaged_in_turn_data".into()));
let executing_move = Arc::new(ExecutingMove::new(
vec![],
1,
p1,
mv.clone(),
mv.move_data().clone(),
ScriptContainer::default(),
));
let mut v = 20_u8;
script.change_base_power(&executing_move, &p2, 0, &mut v).unwrap();
assert_eq!(v, 20_u8);
let s = battle.sides()[1].get_volatile_script(&"double_power_if_target_damaged_in_turn_data".into());
let binding = s.as_ref().unwrap().get().unwrap().read();
let data_script = binding.as_ref().unwrap();
data_script.on_damage(&p2, DamageSource::Misc, 100, 50).unwrap();
let mut v = 20_u8;
script.change_base_power(&executing_move, &p2, 0, &mut v).unwrap();
assert_eq!(v, 40_u8);
data_script.on_end_turn().unwrap();
assert!(!battle.sides()[1].has_volatile_script(&"double_power_if_target_damaged_in_turn_data".into()));
}