|
| 1 | +/* |
| 2 | +Copyright 2025 The Hyperlight Authors. |
| 3 | +
|
| 4 | +Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | +you may not use this file except in compliance with the License. |
| 6 | +You may obtain a copy of the License at |
| 7 | +
|
| 8 | + http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | +
|
| 10 | +Unless required by applicable law or agreed to in writing, software |
| 11 | +distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | +See the License for the specific language governing permissions and |
| 14 | +limitations under the License. |
| 15 | + */ |
| 16 | +#![allow(clippy::disallowed_macros)] |
| 17 | + |
| 18 | +use std::sync::{Arc, Mutex}; |
| 19 | + |
| 20 | +use hyperlight_common::resource::BorrowedResourceGuard; |
| 21 | +use hyperlight_host::{GuestBinary, MultiUseGuestCallContext, UninitializedSandbox}; |
| 22 | +use hyperlight_testing::wit_guest_as_string; |
| 23 | + |
| 24 | +extern crate alloc; |
| 25 | +mod bindings { |
| 26 | + hyperlight_component_macro::host_bindgen!("../tests/rust_guests/witguest/interface.wasm"); |
| 27 | +} |
| 28 | + |
| 29 | +use bindings::*; |
| 30 | + |
| 31 | +struct Host {} |
| 32 | + |
| 33 | +impl test::wit::Roundtrip for Host { |
| 34 | + fn roundtrip_bool(&mut self, x: bool) -> bool { |
| 35 | + x |
| 36 | + } |
| 37 | + fn roundtrip_s8(&mut self, x: i8) -> i8 { |
| 38 | + x |
| 39 | + } |
| 40 | + fn roundtrip_s16(&mut self, x: i16) -> i16 { |
| 41 | + x |
| 42 | + } |
| 43 | + fn roundtrip_s32(&mut self, x: i32) -> i32 { |
| 44 | + x |
| 45 | + } |
| 46 | + fn roundtrip_s64(&mut self, x: i64) -> i64 { |
| 47 | + x |
| 48 | + } |
| 49 | + fn roundtrip_u8(&mut self, x: u8) -> u8 { |
| 50 | + x |
| 51 | + } |
| 52 | + fn roundtrip_u16(&mut self, x: u16) -> u16 { |
| 53 | + x |
| 54 | + } |
| 55 | + fn roundtrip_u32(&mut self, x: u32) -> u32 { |
| 56 | + x |
| 57 | + } |
| 58 | + fn roundtrip_u64(&mut self, x: u64) -> u64 { |
| 59 | + x |
| 60 | + } |
| 61 | + fn roundtrip_f32(&mut self, x: f32) -> f32 { |
| 62 | + x |
| 63 | + } |
| 64 | + fn roundtrip_f64(&mut self, x: f64) -> f64 { |
| 65 | + x |
| 66 | + } |
| 67 | + fn roundtrip_char(&mut self, x: char) -> char { |
| 68 | + x |
| 69 | + } |
| 70 | + fn roundtrip_string(&mut self, x: alloc::string::String) -> alloc::string::String { |
| 71 | + x |
| 72 | + } |
| 73 | + fn roundtrip_list(&mut self, x: alloc::vec::Vec<u8>) -> alloc::vec::Vec<u8> { |
| 74 | + x |
| 75 | + } |
| 76 | + fn roundtrip_tuple(&mut self, x: (alloc::string::String, u8)) -> (alloc::string::String, u8) { |
| 77 | + x |
| 78 | + } |
| 79 | + fn roundtrip_option( |
| 80 | + &mut self, |
| 81 | + x: ::core::option::Option<alloc::string::String>, |
| 82 | + ) -> ::core::option::Option<alloc::string::String> { |
| 83 | + x |
| 84 | + } |
| 85 | + fn roundtrip_result( |
| 86 | + &mut self, |
| 87 | + x: ::core::result::Result<char, alloc::string::String>, |
| 88 | + ) -> ::core::result::Result<char, alloc::string::String> { |
| 89 | + x |
| 90 | + } |
| 91 | + fn roundtrip_record( |
| 92 | + &mut self, |
| 93 | + x: test::wit::roundtrip::Testrecord, |
| 94 | + ) -> test::wit::roundtrip::Testrecord { |
| 95 | + x |
| 96 | + } |
| 97 | + fn roundtrip_flags_small( |
| 98 | + &mut self, |
| 99 | + x: test::wit::roundtrip::Smallflags, |
| 100 | + ) -> test::wit::roundtrip::Smallflags { |
| 101 | + x |
| 102 | + } |
| 103 | + fn roundtrip_flags_large( |
| 104 | + &mut self, |
| 105 | + x: test::wit::roundtrip::Largeflags, |
| 106 | + ) -> test::wit::roundtrip::Largeflags { |
| 107 | + x |
| 108 | + } |
| 109 | + fn roundtrip_variant( |
| 110 | + &mut self, |
| 111 | + x: test::wit::roundtrip::Testvariant, |
| 112 | + ) -> test::wit::roundtrip::Testvariant { |
| 113 | + x |
| 114 | + } |
| 115 | + fn roundtrip_enum( |
| 116 | + &mut self, |
| 117 | + x: test::wit::roundtrip::Testenum, |
| 118 | + ) -> test::wit::roundtrip::Testenum { |
| 119 | + x |
| 120 | + } |
| 121 | +} |
| 122 | + |
| 123 | +struct TestResource { |
| 124 | + n_calls: u32, |
| 125 | + x: String, |
| 126 | + last: char, |
| 127 | +} |
| 128 | + |
| 129 | +use std::sync::atomic::AtomicBool; |
| 130 | +use std::sync::atomic::Ordering::Relaxed; |
| 131 | +// We only have 1 test that uses this, and it isn't a proptest or |
| 132 | +// anything, so it should only run once. If multiple tests using this |
| 133 | +// could run in parallel, there would be problems. |
| 134 | +static HAS_BEEN_DROPPED: AtomicBool = AtomicBool::new(false); |
| 135 | + |
| 136 | +impl Drop for TestResource { |
| 137 | + fn drop(&mut self) { |
| 138 | + assert_eq!(self.x, "strabc"); |
| 139 | + assert_eq!(self.last, 'c'); |
| 140 | + assert!(!HAS_BEEN_DROPPED.swap(true, Relaxed)); |
| 141 | + } |
| 142 | +} |
| 143 | + |
| 144 | +impl test::wit::host_resource::Testresource for Host { |
| 145 | + type T = Arc<Mutex<TestResource>>; |
| 146 | + fn new(&mut self, x: String, last: char) -> Self::T { |
| 147 | + Arc::new(Mutex::new(TestResource { |
| 148 | + n_calls: 0, |
| 149 | + x, |
| 150 | + last, |
| 151 | + })) |
| 152 | + } |
| 153 | + fn append_char(&mut self, self_: BorrowedResourceGuard<'_, Self::T>, c: char) { |
| 154 | + let mut self_ = self_.lock().unwrap(); |
| 155 | + match self_.n_calls { |
| 156 | + // These line up to the initial values and calls made by |
| 157 | + // witguest.rs. Mostly, this just checks that (even after |
| 158 | + // round-tripping an owned reference through the host), we |
| 159 | + // do always seem to get the correct structure. |
| 160 | + 0 => { |
| 161 | + assert_eq!(self_.x, "str"); |
| 162 | + assert_eq!(self_.last, 'z'); |
| 163 | + } |
| 164 | + 1 => { |
| 165 | + assert_eq!(self_.x, "stra"); |
| 166 | + assert_eq!(self_.last, 'a'); |
| 167 | + } |
| 168 | + 2 => { |
| 169 | + assert_eq!(self_.x, "strab"); |
| 170 | + assert_eq!(self_.last, 'b'); |
| 171 | + } |
| 172 | + _ => panic!(), |
| 173 | + }; |
| 174 | + self_.n_calls += 1; |
| 175 | + self_.x.push(c); |
| 176 | + self_.last = c; |
| 177 | + } |
| 178 | +} |
| 179 | + |
| 180 | +impl test::wit::HostResource for Host { |
| 181 | + fn roundtrip_own(&mut self, owned: Arc<Mutex<TestResource>>) -> Arc<Mutex<TestResource>> { |
| 182 | + owned |
| 183 | + } |
| 184 | + |
| 185 | + fn return_own(&mut self, _: Arc<Mutex<TestResource>>) { |
| 186 | + // Not much to do here other than let it be dropped |
| 187 | + } |
| 188 | +} |
| 189 | + |
| 190 | +#[allow(refining_impl_trait)] |
| 191 | +impl test::wit::TestImports for Host { |
| 192 | + type Roundtrip = Self; |
| 193 | + fn roundtrip(&mut self) -> &mut Self { |
| 194 | + self |
| 195 | + } |
| 196 | + type HostResource = Self; |
| 197 | + fn host_resource(&mut self) -> &mut Self { |
| 198 | + self |
| 199 | + } |
| 200 | +} |
| 201 | + |
| 202 | +fn sb() -> TestSandbox<Host, MultiUseGuestCallContext> { |
| 203 | + let path = wit_guest_as_string().unwrap(); |
| 204 | + let guest_path = GuestBinary::FilePath(path); |
| 205 | + let uninit = UninitializedSandbox::new(guest_path, None).unwrap(); |
| 206 | + test::wit::Test::instantiate(uninit, Host {}) |
| 207 | +} |
| 208 | + |
| 209 | +mod wit_test { |
| 210 | + |
| 211 | + use proptest::prelude::*; |
| 212 | + |
| 213 | + use crate::bindings::test::wit::{Roundtrip, TestExports, TestHostResource, roundtrip}; |
| 214 | + use crate::sb; |
| 215 | + |
| 216 | + prop_compose! { |
| 217 | + fn arb_testrecord()(contents in ".*", length in any::<u64>()) -> roundtrip::Testrecord { |
| 218 | + roundtrip::Testrecord { contents, length } |
| 219 | + } |
| 220 | + } |
| 221 | + |
| 222 | + prop_compose! { |
| 223 | + fn arb_smallflags()(flag_a: bool, flag_b: bool, flag_c: bool) -> roundtrip::Smallflags { |
| 224 | + roundtrip::Smallflags { flag_a, flag_b, flag_c } |
| 225 | + } |
| 226 | + } |
| 227 | + |
| 228 | + prop_compose! { |
| 229 | + fn arb_largeflags()( |
| 230 | + flag00: bool, flag01: bool, flag02: bool, flag03: bool, flag04: bool, flag05: bool, flag06: bool, flag07: bool, |
| 231 | + flag08: bool, flag09: bool, flag0a: bool, flag0b: bool, flag0c: bool, flag0d: bool, flag0e: bool, flag0f: bool, |
| 232 | + |
| 233 | + flag10: bool, flag11: bool, flag12: bool, flag13: bool, flag14: bool, flag15: bool, flag16: bool, flag17: bool, |
| 234 | + flag18: bool, flag19: bool, flag1a: bool, flag1b: bool, flag1c: bool, flag1d: bool, flag1e: bool, flag1f: bool, |
| 235 | + ) -> roundtrip::Largeflags { |
| 236 | + roundtrip::Largeflags { |
| 237 | + flag00, flag01, flag02, flag03, flag04, flag05, flag06, flag07, |
| 238 | + flag08, flag09, flag0a, flag0b, flag0c, flag0d, flag0e, flag0f, |
| 239 | + |
| 240 | + flag10, flag11, flag12, flag13, flag14, flag15, flag16, flag17, |
| 241 | + flag18, flag19, flag1a, flag1b, flag1c, flag1d, flag1e, flag1f, |
| 242 | + } |
| 243 | + } |
| 244 | + } |
| 245 | + |
| 246 | + fn arb_testvariant() -> impl Strategy<Value = roundtrip::Testvariant> { |
| 247 | + use roundtrip::Testvariant::*; |
| 248 | + prop_oneof![ |
| 249 | + Just(VariantA), |
| 250 | + any::<String>().prop_map(VariantB), |
| 251 | + any::<char>().prop_map(VariantC), |
| 252 | + ] |
| 253 | + } |
| 254 | + |
| 255 | + fn arb_testenum() -> impl Strategy<Value = roundtrip::Testenum> { |
| 256 | + use roundtrip::Testenum::*; |
| 257 | + prop_oneof![Just(EnumA), Just(EnumB), Just(EnumC),] |
| 258 | + } |
| 259 | + |
| 260 | + macro_rules! make_test { |
| 261 | + ($fn:ident, $($ty:tt)*) => { |
| 262 | + proptest! { |
| 263 | + #[test] |
| 264 | + fn $fn(x $($ty)*) { |
| 265 | + assert_eq!(x, sb().roundtrip().$fn(x.clone())) |
| 266 | + } |
| 267 | + } |
| 268 | + } |
| 269 | + } |
| 270 | + |
| 271 | + make_test! { roundtrip_bool, : bool } |
| 272 | + make_test! { roundtrip_u8, : u8 } |
| 273 | + make_test! { roundtrip_u16, : u16 } |
| 274 | + make_test! { roundtrip_u32, : u32 } |
| 275 | + make_test! { roundtrip_u64, : u64 } |
| 276 | + make_test! { roundtrip_s8, : i8 } |
| 277 | + make_test! { roundtrip_s16, : i16 } |
| 278 | + make_test! { roundtrip_s32, : i32 } |
| 279 | + make_test! { roundtrip_s64, : i64 } |
| 280 | + make_test! { roundtrip_f32, : f32 } |
| 281 | + make_test! { roundtrip_f64, : f64 } |
| 282 | + make_test! { roundtrip_char, : char } |
| 283 | + make_test! { roundtrip_string, : String } |
| 284 | + |
| 285 | + make_test! { roundtrip_list, : Vec<u8> } |
| 286 | + make_test! { roundtrip_tuple, : (String, u8) } |
| 287 | + make_test! { roundtrip_option, : Option<String> } |
| 288 | + make_test! { roundtrip_result, : Result<char, String> } |
| 289 | + |
| 290 | + make_test! { roundtrip_record, in arb_testrecord() } |
| 291 | + make_test! { roundtrip_flags_small, in arb_smallflags() } |
| 292 | + make_test! { roundtrip_flags_large, in arb_largeflags() } |
| 293 | + make_test! { roundtrip_variant, in arb_testvariant() } |
| 294 | + make_test! { roundtrip_enum, in arb_testenum() } |
| 295 | + |
| 296 | + #[test] |
| 297 | + fn test_host_resource() { |
| 298 | + { |
| 299 | + sb().test_host_resource().test(); |
| 300 | + } |
| 301 | + use std::sync::atomic::Ordering::Relaxed; |
| 302 | + assert!(crate::HAS_BEEN_DROPPED.load(Relaxed)); |
| 303 | + } |
| 304 | +} |
0 commit comments