1use crate::function::Function;
4use mun_abi as abi;
5use mun_capi_utils::{
6 error::ErrorHandle, mun_error_try, try_convert_c_string, try_deref, try_deref_mut,
7};
8use mun_memory::{ffi::Type, type_table::TypeTable, Type as RustType};
9use mun_runtime::{FunctionDefinition, FunctionPrototype, FunctionSignature};
10use std::{ffi::c_void, mem::ManuallyDrop, ops::Deref, os::raw::c_char, slice};
11
12#[repr(C)]
14#[derive(Clone, Copy)]
15pub struct Runtime(pub *mut c_void);
16
17impl Runtime {
18 pub(crate) unsafe fn inner(&self) -> Result<&mun_runtime::Runtime, &'static str> {
24 (self.0 as *mut mun_runtime::Runtime)
25 .as_ref()
26 .ok_or("null pointer")
27 }
28
29 pub unsafe fn inner_mut(&self) -> Result<&mut mun_runtime::Runtime, &'static str> {
36 (self.0 as *mut mun_runtime::Runtime)
37 .as_mut()
38 .ok_or("null pointer")
39 }
40}
41
42#[repr(C)]
46#[derive(Clone)]
47pub struct ExternalFunctionDefinition {
48 pub name: *const c_char,
50
51 pub num_args: u32,
53
54 pub arg_types: *const Type,
56
57 pub return_type: Type,
59
60 pub fn_ptr: *const c_void,
62}
63
64#[repr(C)]
71#[derive(Clone, Copy)]
72pub struct RuntimeOptions {
73 pub functions: *const ExternalFunctionDefinition,
79
80 pub num_functions: u32,
82}
83
84impl Default for RuntimeOptions {
85 fn default() -> Self {
86 RuntimeOptions {
87 functions: std::ptr::null(),
88 num_functions: 0,
89 }
90 }
91}
92
93#[no_mangle]
106pub unsafe extern "C" fn mun_runtime_create(
107 library_path: *const c_char,
108 options: RuntimeOptions,
109 handle: *mut Runtime,
110) -> ErrorHandle {
111 let library_path = mun_error_try!(try_convert_c_string(library_path)
112 .map_err(|e| format!("invalid argument 'library_path': {e}")));
113 let handle = try_deref_mut!(handle);
114
115 if options.num_functions > 0 && options.functions.is_null() {
116 return ErrorHandle::new("invalid argument: 'functions' is null pointer.");
117 }
118
119 let type_table = TypeTable::default();
120 let user_functions = mun_error_try!(std::slice::from_raw_parts(
121 options.functions,
122 options.num_functions as usize
123 )
124 .iter()
125 .map(|def| {
126 let name =
127 try_convert_c_string(def.name).map_err(|e| format!("invalid function name: {e}"))?;
128 let return_type = ManuallyDrop::new(
129 def.return_type
130 .to_owned()
131 .map_err(|e| format!("invalid function '{name}': 'return_type': {e}"))?,
132 )
133 .deref()
134 .clone();
135
136 if def.num_args > 0 && def.arg_types.is_null() {
137 return Err(format!(
138 "invalid function '{}': 'arg_types' is null pointer.",
139 name
140 ));
141 }
142
143 let arg_types: Vec<_> = if def.num_args > 0 {
144 std::slice::from_raw_parts(def.arg_types, def.num_args as usize)
145 .iter()
146 .enumerate()
147 .map(|(i, arg)| -> Result<RustType, String> {
148 let ty = (*arg).to_owned().map_err(|e| {
149 format!("invalid function '{}': argument #{}: {}", name, i + 1, e)
150 })?;
151 Ok(ManuallyDrop::new(ty).deref().clone())
152 })
153 .collect::<Result<_, _>>()?
154 } else {
155 Vec::new()
156 };
157
158 Ok(FunctionDefinition {
159 prototype: FunctionPrototype {
160 name: name.to_owned(),
161 signature: FunctionSignature {
162 arg_types,
163 return_type,
164 },
165 },
166 fn_ptr: def.fn_ptr,
167 })
168 })
169 .collect::<Result<_, _>>());
170
171 let runtime_options = mun_runtime::RuntimeOptions {
172 library_path: library_path.into(),
173 user_functions,
174 type_table,
175 };
176
177 let runtime = match mun_runtime::Runtime::new(runtime_options) {
178 Ok(runtime) => runtime,
179 Err(e) => return ErrorHandle::new(format!("{:?}", e)),
180 };
181
182 handle.0 = Box::into_raw(Box::new(runtime)) as *mut _;
183 ErrorHandle::default()
184}
185
186#[no_mangle]
188pub extern "C" fn mun_runtime_destroy(runtime: Runtime) -> ErrorHandle {
189 if runtime.0.is_null() {
190 return ErrorHandle::new("invalid argument 'runtime': null pointer");
191 }
192 let _runtime = unsafe { Box::from_raw(runtime.0) };
193 ErrorHandle::default()
194}
195
196#[no_mangle]
207pub unsafe extern "C" fn mun_runtime_find_function_definition(
208 runtime: Runtime,
209 fn_name: *const c_char,
210 fn_name_len: usize,
211 has_fn_info: *mut bool,
212 fn_info: *mut Function,
213) -> ErrorHandle {
214 let runtime = mun_error_try!(runtime
215 .inner()
216 .map_err(|e| format!("invalid argument 'runtime': {e}")));
217 if fn_name.is_null() {
218 return ErrorHandle::new("invalid argument 'fn_name': null pointer");
219 }
220 let name = mun_error_try!(std::str::from_utf8(slice::from_raw_parts(
221 fn_name as *const u8,
222 fn_name_len
223 ))
224 .map_err(|_| String::from("invalid argument 'fn_name': invalid UTF-8 encoded")));
225 let has_fn_info = try_deref_mut!(has_fn_info);
226 let fn_info = try_deref_mut!(fn_info);
227 match runtime.get_function_definition(name) {
228 Some(info) => {
229 *has_fn_info = true;
230 *fn_info = info.into()
231 }
232 None => *has_fn_info = false,
233 }
234
235 ErrorHandle::default()
236}
237
238#[no_mangle]
250pub unsafe extern "C" fn mun_runtime_get_type_info_by_name(
251 runtime: Runtime,
252 type_name: *const c_char,
253 has_type_info: *mut bool,
254 type_info: *mut Type,
255) -> ErrorHandle {
256 let runtime = mun_error_try!(runtime
257 .inner()
258 .map_err(|e| format!("invalid argument 'runtime': {e}")));
259 let type_name =
260 mun_error_try!(try_convert_c_string(type_name)
261 .map_err(|e| format!("invalid argument 'type_name': {e}")));
262 let has_type_info = try_deref_mut!(has_type_info);
263 let type_info = try_deref_mut!(type_info);
264 match runtime.get_type_info_by_name(type_name) {
265 Some(info) => {
266 *has_type_info = true;
267 *type_info = info.into();
268 }
269 None => *has_type_info = false,
270 }
271
272 ErrorHandle::default()
273}
274
275#[no_mangle]
287pub unsafe extern "C" fn mun_runtime_get_type_info_by_id(
288 runtime: Runtime,
289 type_id: *const abi::TypeId,
290 has_type_info: *mut bool,
291 type_info: *mut Type,
292) -> ErrorHandle {
293 let runtime = mun_error_try!(runtime
294 .inner()
295 .map_err(|e| format!("invalid argument 'runtime': {e}")));
296 let type_id = try_deref!(type_id);
297 let has_type_info = try_deref_mut!(has_type_info);
298 let type_info = try_deref_mut!(type_info);
299
300 match runtime.get_type_info_by_id(type_id) {
301 Some(info) => {
302 *has_type_info = true;
303 *type_info = info.into();
304 }
305 None => *has_type_info = false,
306 }
307
308 ErrorHandle::default()
309}
310
311#[no_mangle]
322pub unsafe extern "C" fn mun_runtime_update(runtime: Runtime, updated: *mut bool) -> ErrorHandle {
323 let runtime = mun_error_try!(runtime
324 .inner_mut()
325 .map_err(|e| format!("invalid argument 'runtime': {e}")));
326 let updated = try_deref_mut!(updated);
327 *updated = runtime.update();
328 ErrorHandle::default()
329}
330
331#[cfg(test)]
332mod tests {
333 use super::*;
334 use crate::{test_invalid_runtime, test_util::TestDriver};
335 use mun_capi_utils::error::mun_error_destroy;
336 use mun_capi_utils::{assert_error_snapshot, assert_getter1, assert_getter2, assert_getter3};
337 use mun_memory::HasStaticType;
338 use std::{ffi::CString, mem::MaybeUninit, ptr};
339
340 test_invalid_runtime!(
341 runtime_find_function_definition(ptr::null(), 0, ptr::null_mut(), ptr::null_mut()),
342 runtime_get_type_info_by_name(ptr::null(), ptr::null_mut(), ptr::null_mut()),
343 runtime_get_type_info_by_id(ptr::null(), ptr::null_mut(), ptr::null_mut()),
344 runtime_update(ptr::null_mut())
345 );
346
347 #[test]
348 fn test_runtime_create_invalid_lib_path() {
349 assert_error_snapshot!(
350 unsafe { mun_runtime_create(ptr::null(), RuntimeOptions::default(), ptr::null_mut()) },
351 @r###""invalid argument \'library_path\': null pointer""###
352 );
353 }
354
355 #[test]
356 fn test_runtime_create_invalid_lib_path_encoding() {
357 let invalid_encoding = ['�', '\0'];
358
359 assert_error_snapshot!(
360 unsafe {
361 mun_runtime_create(
362 invalid_encoding.as_ptr() as *const _,
363 RuntimeOptions::default(),
364 ptr::null_mut(),
365 )
366 },
367 @r###""invalid argument \'library_path\': invalid UTF-8 encoded""###
368 );
369 }
370
371 #[test]
372 fn test_runtime_create_invalid_functions() {
373 let lib_path = CString::new("some/path").expect("Invalid library path");
374
375 let options = RuntimeOptions {
376 num_functions: 1,
377 ..Default::default()
378 };
379
380 let mut handle = MaybeUninit::uninit();
381 assert_error_snapshot!(
382 unsafe { mun_runtime_create(lib_path.into_raw(), options, handle.as_mut_ptr()) },
383 @r###""invalid argument: \'functions\' is null pointer.""###
384 );
385 }
386
387 #[test]
388 fn test_runtime_create_invalid_handle() {
389 let lib_path = CString::new("some/path").expect("Invalid library path");
390
391 assert_error_snapshot!(
392 unsafe {
393 mun_runtime_create(lib_path.into_raw(), RuntimeOptions::default(), ptr::null_mut())
394 },
395 @r###""invalid argument \'handle\': null pointer""###
396 );
397 }
398
399 #[test]
400 fn test_runtime_create_invalid_user_function_name() {
401 let lib_path = CString::new("some/path").expect("Invalid library path");
402
403 let type_id = <()>::type_info().clone().into();
404 let functions = vec![ExternalFunctionDefinition {
405 name: ptr::null(),
406 arg_types: ptr::null(),
407 return_type: type_id,
408 num_args: 0,
409 fn_ptr: ptr::null(),
410 }];
411
412 let options = RuntimeOptions {
413 functions: functions.as_ptr(),
414 num_functions: 1,
415 ..Default::default()
416 };
417
418 let mut handle = MaybeUninit::uninit();
419 assert_error_snapshot!(
420 unsafe { mun_runtime_create(lib_path.into_raw(), options, handle.as_mut_ptr()) },
421 @r###""invalid function name: null pointer""###
422 );
423 }
424
425 #[test]
426 fn test_runtime_create_invalid_user_function_name_encoding() {
427 let lib_path = CString::new("some/path").expect("Invalid library path");
428
429 let invalid_encoding = ['�', '\0'];
430 let type_id = <()>::type_info().clone().into();
431 let functions = vec![ExternalFunctionDefinition {
432 name: invalid_encoding.as_ptr() as *const _,
433 arg_types: ptr::null(),
434 return_type: type_id,
435 num_args: 0,
436 fn_ptr: ptr::null(),
437 }];
438
439 let options = RuntimeOptions {
440 functions: functions.as_ptr(),
441 num_functions: 1,
442 ..Default::default()
443 };
444
445 let mut handle = MaybeUninit::uninit();
446 assert_error_snapshot!(
447 unsafe { mun_runtime_create(lib_path.into_raw(), options, handle.as_mut_ptr()) },
448 @r###""invalid function name: invalid UTF-8 encoded""###
449 );
450 }
451
452 #[test]
453 fn test_runtime_create_invalid_user_function_return_type() {
454 let lib_path = CString::new("some/path").expect("Invalid library path");
455 let function_name = CString::new("foobar").unwrap();
456
457 let functions = vec![ExternalFunctionDefinition {
458 name: function_name.as_ptr(),
459 arg_types: ptr::null(),
460 return_type: Type::null(),
461 num_args: 0,
462 fn_ptr: ptr::null(),
463 }];
464
465 let options = RuntimeOptions {
466 functions: functions.as_ptr(),
467 num_functions: 1,
468 ..Default::default()
469 };
470
471 let mut handle = MaybeUninit::uninit();
472 assert_error_snapshot!(
473 unsafe { mun_runtime_create(lib_path.into_raw(), options, handle.as_mut_ptr()) },
474 @r###""invalid function \'foobar\': \'return_type\': null pointer""###
475 );
476 }
477
478 #[test]
479 fn test_runtime_create_invalid_user_function_arg_types_ptr() {
480 let lib_path = CString::new("some/path").expect("Invalid library path");
481 let function_name = CString::new("foobar").unwrap();
482
483 let type_id = <()>::type_info().clone().into();
484 let functions = vec![ExternalFunctionDefinition {
485 name: function_name.as_ptr(),
486 arg_types: ptr::null(),
487 return_type: type_id,
488 num_args: 1,
489 fn_ptr: ptr::null(),
490 }];
491
492 let options = RuntimeOptions {
493 functions: functions.as_ptr(),
494 num_functions: 1,
495 ..Default::default()
496 };
497
498 let mut handle = MaybeUninit::uninit();
499 assert_error_snapshot!(
500 unsafe { mun_runtime_create(lib_path.into_raw(), options, handle.as_mut_ptr()) },
501 @r###""invalid function \'foobar\': \'arg_types\' is null pointer.""###
502 );
503 }
504
505 #[test]
506 fn test_runtime_create_invalid_user_function_arg_types() {
507 let lib_path = CString::new("some/path").expect("Invalid library path");
508 let function_name = CString::new("foobar").unwrap();
509 let arg_types = [Type::null()];
510
511 let type_id = <()>::type_info().clone().into();
512 let functions = vec![ExternalFunctionDefinition {
513 name: function_name.as_ptr(),
514 arg_types: &arg_types as _,
515 return_type: type_id,
516 num_args: 1,
517 fn_ptr: ptr::null(),
518 }];
519
520 let options = RuntimeOptions {
521 functions: functions.as_ptr(),
522 num_functions: 1,
523 ..Default::default()
524 };
525
526 let mut handle = MaybeUninit::uninit();
527 assert_error_snapshot!(
528 unsafe { mun_runtime_create(lib_path.into_raw(), options, handle.as_mut_ptr()) },
529 @r###""invalid function \'foobar\': argument #1: null pointer""###
530 );
531 }
532
533 #[test]
534 fn test_runtime_get_function_info_invalid_fn_name() {
535 let driver = TestDriver::new(
536 r#"
537 pub fn main() -> i32 { 3 }
538 "#,
539 );
540
541 assert_error_snapshot!(
542 unsafe {
543 mun_runtime_find_function_definition(
544 driver.runtime,
545 ptr::null(),
546 0,
547 ptr::null_mut(),
548 ptr::null_mut(),
549 )
550 },
551 @r###""invalid argument \'fn_name\': null pointer""###
552 );
553 }
554
555 #[test]
556 fn test_runtime_get_function_info_invalid_fn_name_encoding() {
557 let driver = TestDriver::new(
558 r#"
559 pub fn main() -> i32 { 3 }
560 "#,
561 );
562
563 let invalid_encoding = ['�', '\0'];
564 assert_error_snapshot!(
565 unsafe {
566 mun_runtime_find_function_definition(
567 driver.runtime,
568 invalid_encoding.as_ptr() as *const _,
569 3,
570 ptr::null_mut(),
571 ptr::null_mut(),
572 )
573 },
574 @r###""invalid argument \'fn_name\': invalid UTF-8 encoded""###
575 );
576 }
577
578 #[test]
579 fn test_runtime_get_function_info_invalid_has_fn_info() {
580 let driver = TestDriver::new(
581 r#"
582 pub fn main() -> i32 { 3 }
583 "#,
584 );
585
586 let fn_name = CString::new("main").expect("Invalid function name");
587 assert_error_snapshot!(
588 unsafe {
589 mun_runtime_find_function_definition(
590 driver.runtime,
591 fn_name.as_ptr(),
592 fn_name.as_bytes().len(),
593 ptr::null_mut(),
594 ptr::null_mut(),
595 )
596 },
597 @r###""invalid argument \'has_fn_info\': null pointer""###
598 );
599 }
600
601 #[test]
602 fn test_runtime_get_function_info_invalid_fn_info() {
603 let driver = TestDriver::new(
604 r#"
605 pub fn main() -> i32 { 3 }
606 "#,
607 );
608
609 let fn_name = CString::new("main").expect("Invalid function name");
610 let mut has_fn_info = MaybeUninit::uninit();
611 assert_error_snapshot!(
612 unsafe {
613 mun_runtime_find_function_definition(
614 driver.runtime,
615 fn_name.as_ptr(),
616 fn_name.as_bytes().len(),
617 has_fn_info.as_mut_ptr(),
618 ptr::null_mut(),
619 )
620 },
621 @r###""invalid argument \'fn_info\': null pointer""###
622 );
623 }
624
625 #[test]
626 fn test_runtime_get_function_info_none() {
627 let driver = TestDriver::new(
628 r#"
629 pub fn main() -> i32 { 3 }
630 "#,
631 );
632
633 let fn_name = CString::new("add").expect("Invalid function name");
634 assert_getter3!(mun_runtime_find_function_definition(
635 driver.runtime,
636 fn_name.as_ptr(),
637 fn_name.as_bytes().len(),
638 has_fn_info,
639 _fn_definition,
640 ));
641 assert!(!has_fn_info);
642 }
643
644 #[test]
645 fn test_runtime_get_function_info_some() {
646 let driver = TestDriver::new(
647 r#"
648 pub fn main() -> i32 { 3 }
649 "#,
650 );
651
652 let fn_name = CString::new("main").expect("Invalid function name");
653 assert_getter3!(mun_runtime_find_function_definition(
654 driver.runtime,
655 fn_name.as_ptr(),
656 fn_name.as_bytes().len(),
657 has_fn_info,
658 _fn_definition,
659 ));
660 assert!(has_fn_info);
661 }
662
663 #[test]
664 fn test_runtime_get_type_info_by_name_invalid_type_name() {
665 let driver = TestDriver::new(
666 r#"
667 pub struct Foo;
668 "#,
669 );
670
671 assert_error_snapshot!(
672 unsafe {
673 mun_runtime_get_type_info_by_name(
674 driver.runtime,
675 ptr::null(),
676 ptr::null_mut(),
677 ptr::null_mut(),
678 )
679 },
680 @r###""invalid argument \'type_name\': null pointer""###
681 );
682 }
683
684 #[test]
685 fn test_runtime_get_type_info_by_name_invalid_type_name_encoding() {
686 let driver = TestDriver::new(
687 r#"
688 pub struct Foo;
689 "#,
690 );
691
692 let invalid_encoding = ['�', '\0'];
693 assert_error_snapshot!(
694 unsafe {
695 mun_runtime_get_type_info_by_name(
696 driver.runtime,
697 invalid_encoding.as_ptr() as *const _,
698 ptr::null_mut(),
699 ptr::null_mut(),
700 )
701 },
702 @r###""invalid argument \'type_name\': invalid UTF-8 encoded""###
703 );
704 }
705
706 #[test]
707 fn test_runtime_get_type_info_by_name_invalid_has_type_info() {
708 let driver = TestDriver::new(
709 r#"
710 pub struct Foo;
711 "#,
712 );
713
714 let type_name = CString::new("Foo").expect("Invalid type name");
715 assert_error_snapshot!(
716 unsafe {
717 mun_runtime_get_type_info_by_name(
718 driver.runtime,
719 type_name.as_ptr(),
720 ptr::null_mut(),
721 ptr::null_mut(),
722 )
723 },
724 @r###""invalid argument \'has_type_info\': null pointer""###
725 );
726 }
727
728 #[test]
729 fn test_runtime_get_type_info_by_name_invalid_type_info() {
730 let driver = TestDriver::new(
731 r#"
732 pub struct Foo;
733 "#,
734 );
735
736 let type_name = CString::new("Foo").expect("Invalid type name");
737 let mut has_type_info = false;
738 assert_error_snapshot!(
739 unsafe {
740 mun_runtime_get_type_info_by_name(
741 driver.runtime,
742 type_name.as_ptr(),
743 &mut has_type_info as *mut _,
744 ptr::null_mut(),
745 )
746 },
747 @r###""invalid argument \'type_info\': null pointer""###
748 );
749 }
750
751 #[test]
752 fn test_runtime_get_type_info_by_name_none() {
753 let driver = TestDriver::new(
754 r#"
755 pub struct Foo;
756 "#,
757 );
758
759 let type_name = CString::new("Bar").expect("Invalid type name");
760 assert_getter2!(mun_runtime_get_type_info_by_name(
761 driver.runtime,
762 type_name.as_ptr(),
763 has_type_info,
764 _type_info,
765 ));
766 assert!(!has_type_info);
767 }
768
769 #[test]
770 fn test_runtime_get_type_info_by_name_some() {
771 let driver = TestDriver::new(
772 r#"
773 pub struct Foo;
774 "#,
775 );
776
777 let type_name = CString::new("Foo").expect("Invalid type name");
778 assert_getter2!(mun_runtime_get_type_info_by_name(
779 driver.runtime,
780 type_name.as_ptr(),
781 has_type_info,
782 _type_info,
783 ));
784 assert!(has_type_info);
785 }
786
787 #[test]
788 fn test_runtime_get_type_info_by_id_invalid_type_id() {
789 let driver = TestDriver::new(
790 r#"
791 pub struct Foo;
792 "#,
793 );
794
795 assert_error_snapshot!(
796 unsafe {
797 mun_runtime_get_type_info_by_id(
798 driver.runtime,
799 ptr::null(),
800 ptr::null_mut(),
801 ptr::null_mut(),
802 )
803 },
804 @r###""invalid argument \'type_id\': null pointer""###
805 );
806 }
807
808 #[test]
809 fn test_runtime_get_type_info_by_id_invalid_has_type_info() {
810 let driver = TestDriver::new(
811 r#"
812 pub struct Foo;
813 "#,
814 );
815
816 let type_id = abi::TypeId::Concrete(abi::Guid([0; 16]));
817 assert_error_snapshot!(
818 unsafe {
819 mun_runtime_get_type_info_by_id(
820 driver.runtime,
821 &type_id as *const abi::TypeId,
822 ptr::null_mut(),
823 ptr::null_mut(),
824 )
825 },
826 @r###""invalid argument \'has_type_info\': null pointer""###
827 );
828 }
829
830 #[test]
831 fn test_runtime_get_type_info_by_id_invalid_type_info() {
832 let driver = TestDriver::new(
833 r#"
834 pub struct Foo;
835 "#,
836 );
837
838 let type_id = abi::TypeId::Concrete(abi::Guid([0; 16]));
839 let mut has_type_info = false;
840 assert_error_snapshot!(
841 unsafe {
842 mun_runtime_get_type_info_by_id(
843 driver.runtime,
844 &type_id as *const abi::TypeId,
845 &mut has_type_info as *mut _,
846 ptr::null_mut(),
847 )
848 },
849 @r###""invalid argument \'type_info\': null pointer""###
850 );
851 }
852
853 #[test]
854 fn test_runtime_get_type_info_by_id_none() {
855 let driver = TestDriver::new(
856 r#"
857 pub struct Foo;
858 "#,
859 );
860
861 let type_id = abi::TypeId::Concrete(abi::Guid([0; 16]));
862 assert_getter2!(mun_runtime_get_type_info_by_id(
863 driver.runtime,
864 &type_id as *const abi::TypeId,
865 has_type_info,
866 _type_info,
867 ));
868 assert!(!has_type_info);
869 }
870
871 #[test]
872 fn test_runtime_get_type_info_by_id_some() {
873 let driver = TestDriver::new(
874 r#"
875 pub struct Foo;
876 "#,
877 );
878
879 let type_name = CString::new("Foo").expect("Invalid type name");
880 assert_getter2!(mun_runtime_get_type_info_by_name(
881 driver.runtime,
882 type_name.as_ptr(),
883 has_type_info,
884 _type_info,
885 ));
886 assert!(has_type_info);
887 }
888
889 #[test]
890 fn test_runtime_update_invalid_updated() {
891 let driver = TestDriver::new(
892 r#"
893 pub fn main() -> i32 { 3 }
894 "#,
895 );
896
897 assert_error_snapshot!(
898 unsafe { mun_runtime_update(driver.runtime, ptr::null_mut()) },
899 @r###""invalid argument \'updated\': null pointer""###
900 );
901 }
902
903 #[test]
904 fn test_runtime_update() {
905 let driver = TestDriver::new(
906 r#"
907 pub fn main() -> i32 { 3 }
908 "#,
909 );
910
911 assert_getter1!(mun_runtime_update(driver.runtime, _updated));
912 }
913}