|
6 | 6 | */
|
7 | 7 |
|
8 | 8 | use std::cmp::Ordering;
|
| 9 | +use std::fmt; |
9 | 10 | use std::fmt::Display;
|
10 | 11 |
|
11 | 12 | use godot::builtin::{
|
12 |
| - array, dict, varray, vslice, Array, GString, NodePath, Signal, StringName, Variant, Vector2, |
13 |
| - Vector3, |
| 13 | + array, dict, varray, vslice, Array, Color, GString, NodePath, PackedInt32Array, |
| 14 | + PackedStringArray, Projection, Quaternion, Signal, StringName, Transform2D, Transform3D, |
| 15 | + Variant, Vector2, Vector2i, Vector3, Vector3i, |
14 | 16 | };
|
15 | 17 | use godot::builtin::{Basis, Dictionary, VariantArray, VariantOperator, VariantType};
|
16 |
| -use godot::classes::{Node, Node2D}; |
| 18 | +use godot::classes::{Node, Node2D, Resource}; |
17 | 19 | use godot::meta::{FromGodot, ToGodot};
|
18 |
| -use godot::obj::{Gd, InstanceId, NewAlloc}; |
| 20 | +use godot::obj::{Gd, InstanceId, NewAlloc, NewGd}; |
19 | 21 | use godot::sys::GodotFfi;
|
20 | 22 |
|
21 | 23 | use crate::common::roundtrip;
|
@@ -75,6 +77,117 @@ fn variant_conversions() {
|
75 | 77 | roundtrip(Signal::invalid());
|
76 | 78 | }
|
77 | 79 |
|
| 80 | +#[itest] |
| 81 | +fn variant_relaxed_conversions() { |
| 82 | + // See https://github.com/godotengine/godot/blob/4.4-stable/core/variant/variant.cpp#L532. |
| 83 | + |
| 84 | + let obj = Node::new_alloc(); |
| 85 | + |
| 86 | + // reflexive |
| 87 | + convert_relaxed_to(-22i8, -22i8); |
| 88 | + convert_relaxed_to("some str", GString::from("some str")); |
| 89 | + convert_relaxed_to(TEST_BASIS, TEST_BASIS); |
| 90 | + convert_relaxed_to(obj.clone(), obj.clone()); |
| 91 | + |
| 92 | + // int <-> float |
| 93 | + convert_relaxed_to(1234567890i64, 1234567890f64); |
| 94 | + convert_relaxed_to(1234567890f64, 1234567890i64); |
| 95 | + |
| 96 | + // int <-> bool |
| 97 | + convert_relaxed_to(-123, true); |
| 98 | + convert_relaxed_to(0, false); |
| 99 | + convert_relaxed_to(true, 1); |
| 100 | + convert_relaxed_to(false, 0); |
| 101 | + |
| 102 | + // float <-> bool |
| 103 | + convert_relaxed_to(123.45, true); |
| 104 | + convert_relaxed_to(0.0, false); |
| 105 | + convert_relaxed_to(true, 1.0); |
| 106 | + convert_relaxed_to(false, 0.0); |
| 107 | + |
| 108 | + // GString <-> StringName |
| 109 | + convert_relaxed_to("hello", StringName::from("hello")); |
| 110 | + convert_relaxed_to(StringName::from("hello"), GString::from("hello")); |
| 111 | + |
| 112 | + // GString <-> NodePath |
| 113 | + convert_relaxed_to("hello", NodePath::from("hello")); |
| 114 | + convert_relaxed_to(NodePath::from("hello"), GString::from("hello")); |
| 115 | + |
| 116 | + // anything -> nil |
| 117 | + convert_relaxed_to(Variant::nil(), Variant::nil()); |
| 118 | + convert_relaxed_to((), Variant::nil()); |
| 119 | + convert_relaxed_fail::<()>(obj.clone()); |
| 120 | + convert_relaxed_fail::<()>(123.45); |
| 121 | + convert_relaxed_fail::<()>(Vector3i::new(1, 2, 3)); |
| 122 | + |
| 123 | + // nil -> anything (except Variant) - fails |
| 124 | + convert_relaxed_fail::<i64>(Variant::nil()); |
| 125 | + convert_relaxed_fail::<GString>(Variant::nil()); |
| 126 | + convert_relaxed_fail::<Gd<Node>>(Variant::nil()); |
| 127 | + convert_relaxed_fail::<VariantArray>(Variant::nil()); |
| 128 | + convert_relaxed_fail::<Dictionary>(Variant::nil()); |
| 129 | + |
| 130 | + // Array -> Packed*Array |
| 131 | + let packed_ints = PackedInt32Array::from([1, 2, 3]); |
| 132 | + let packed_strings = PackedStringArray::from(["a".into(), "bb".into()]); |
| 133 | + let strings: Array<GString> = array!["a", "bb"]; |
| 134 | + |
| 135 | + convert_relaxed_to(array![1, 2, 3], packed_ints.clone()); |
| 136 | + convert_relaxed_to(varray![1, 2, 3], packed_ints.clone()); |
| 137 | + convert_relaxed_to(strings.clone(), packed_strings.clone()); |
| 138 | + convert_relaxed_to(varray!["a", "bb"], packed_strings.clone()); |
| 139 | + |
| 140 | + // Packed*Array -> Array |
| 141 | + convert_relaxed_to(packed_ints.clone(), array![1, 2, 3]); |
| 142 | + convert_relaxed_to(packed_ints, varray![1, 2, 3]); |
| 143 | + convert_relaxed_to(packed_strings.clone(), strings); |
| 144 | + convert_relaxed_to(packed_strings, varray!["a", "bb"]); |
| 145 | + |
| 146 | + // Object|nil -> optional Object |
| 147 | + convert_relaxed_to(obj.clone(), Some(obj.clone())); |
| 148 | + convert_relaxed_to(Variant::nil(), Option::<Gd<Node>>::None); |
| 149 | + |
| 150 | + // Object -> Rid |
| 151 | + let res = Resource::new_gd(); |
| 152 | + let rid = res.get_rid(); |
| 153 | + convert_relaxed_to(res.clone(), rid); |
| 154 | + |
| 155 | + // Vector2 <-> Vector2i |
| 156 | + convert_relaxed_to(Vector2::new(1.0, 2.0), Vector2i::new(1, 2)); |
| 157 | + convert_relaxed_to(Vector2i::new(1, 2), Vector2::new(1.0, 2.0)); |
| 158 | + |
| 159 | + // int|String -> Color (don't use float colors due to rounding errors / 255-vs-256 imprecision). |
| 160 | + convert_relaxed_to(0xFF_80_00_40u32, Color::from_rgba8(255, 128, 0, 64)); |
| 161 | + convert_relaxed_to("MEDIUM_AQUAMARINE", Color::MEDIUM_AQUAMARINE); |
| 162 | + |
| 163 | + // Everything -> Transform3D |
| 164 | + convert_relaxed_to(Transform2D::IDENTITY, Transform3D::IDENTITY); |
| 165 | + convert_relaxed_to(Basis::IDENTITY, Transform3D::IDENTITY); |
| 166 | + convert_relaxed_to(Quaternion::IDENTITY, Transform3D::IDENTITY); |
| 167 | + |
| 168 | + // Projection <-> Transform3D |
| 169 | + convert_relaxed_to(Projection::IDENTITY, Transform3D::IDENTITY); |
| 170 | + convert_relaxed_to(Transform3D::IDENTITY, Projection::IDENTITY); |
| 171 | + |
| 172 | + // Quaternion <-> Basis |
| 173 | + convert_relaxed_to(Basis::IDENTITY, Quaternion::IDENTITY); |
| 174 | + convert_relaxed_to(Quaternion::IDENTITY, Basis::IDENTITY); |
| 175 | + |
| 176 | + // Other geometric conversions between the above fail. |
| 177 | + convert_relaxed_fail::<Transform2D>(Projection::IDENTITY); |
| 178 | + convert_relaxed_fail::<Transform2D>(Quaternion::IDENTITY); |
| 179 | + convert_relaxed_fail::<Transform2D>(Basis::IDENTITY); |
| 180 | + convert_relaxed_fail::<Projection>(Transform2D::IDENTITY); |
| 181 | + convert_relaxed_fail::<Projection>(Quaternion::IDENTITY); |
| 182 | + convert_relaxed_fail::<Projection>(Basis::IDENTITY); |
| 183 | + convert_relaxed_fail::<Quaternion>(Transform2D::IDENTITY); |
| 184 | + convert_relaxed_fail::<Quaternion>(Projection::IDENTITY); |
| 185 | + convert_relaxed_fail::<Basis>(Transform2D::IDENTITY); |
| 186 | + convert_relaxed_fail::<Basis>(Projection::IDENTITY); |
| 187 | + |
| 188 | + obj.free(); |
| 189 | +} |
| 190 | + |
78 | 191 | #[itest]
|
79 | 192 | fn variant_bad_integer_conversions() {
|
80 | 193 | truncate_bad::<i8>(128);
|
@@ -550,6 +663,45 @@ fn variant_hash() {
|
550 | 663 |
|
551 | 664 | // ----------------------------------------------------------------------------------------------------------------------------------------------
|
552 | 665 |
|
| 666 | +fn convert_relaxed_to<T, U>(from: T, expected_to: U) |
| 667 | +where |
| 668 | + T: ToGodot + fmt::Debug, |
| 669 | + U: FromGodot + PartialEq + fmt::Debug, |
| 670 | +{ |
| 671 | + let variant = from.to_variant(); |
| 672 | + let result = variant.try_to_relaxed::<U>(); |
| 673 | + |
| 674 | + match result { |
| 675 | + Ok(to) => { |
| 676 | + assert_eq!( |
| 677 | + to, expected_to, |
| 678 | + "converting {from:?} to {to:?} resulted in unexpected value" |
| 679 | + ); |
| 680 | + } |
| 681 | + Err(err) => { |
| 682 | + panic!("Conversion from {from:?} to {expected_to:?} failed: {err}"); |
| 683 | + } |
| 684 | + } |
| 685 | +} |
| 686 | + |
| 687 | +fn convert_relaxed_fail<U>(from: impl ToGodot + fmt::Debug) |
| 688 | +where |
| 689 | + U: FromGodot + PartialEq + fmt::Debug, |
| 690 | +{ |
| 691 | + let variant = from.to_variant(); |
| 692 | + let result = variant.try_to_relaxed::<U>(); |
| 693 | + |
| 694 | + match result { |
| 695 | + Ok(to) => { |
| 696 | + let to_type = godot::sys::short_type_name::<U>(); |
| 697 | + panic!( |
| 698 | + "Conversion from {from:?} to {to_type:?} unexpectedly succeeded with value: {to:?}" |
| 699 | + ); |
| 700 | + } |
| 701 | + Err(_err) => {} |
| 702 | + } |
| 703 | +} |
| 704 | + |
553 | 705 | fn truncate_bad<T>(original_value: i64)
|
554 | 706 | where
|
555 | 707 | T: FromGodot + Display,
|
|
0 commit comments