1use std::{fs::metadata, path::Path};
53
54#[cfg(feature = "icmp")]
55use std::net::IpAddr;
56use std::net::TcpStream;
57
58use proc_macro::TokenStream;
59use proc_macro_error2::abort_call_site;
60use proc_macro_error2::proc_macro_error;
61use syn::{parse_macro_input, ItemFn, ItemMod};
62
63#[cfg(feature = "runtime")]
64use syn::{Item, ItemStruct, ItemType};
65
66#[cfg(feature = "executable")]
67use which::which;
68
69use crate::utils::{fn_macro, is_module, lock_macro, mod_macro, sanitize_env_vars_attr};
70
71mod utils;
72
73#[proc_macro_attribute]
113#[proc_macro_error]
114pub fn env(attr: TokenStream, stream: TokenStream) -> TokenStream {
115 if is_module(&stream) {
116 mod_macro(
117 attr,
118 parse_macro_input!(stream as ItemMod),
119 check_env_condition,
120 )
121 } else {
122 fn_macro(
123 attr,
124 parse_macro_input!(stream as ItemFn),
125 check_env_condition,
126 )
127 }
128}
129
130fn check_env_condition(attr_str: String) -> (bool, String) {
131 let var_names = sanitize_env_vars_attr(&attr_str);
132
133 let mut missing_vars = vec![];
135 for name in var_names {
136 if std::env::var(name).is_err() {
137 missing_vars.push(name.to_string());
138 }
139 }
140
141 let ignore_msg = if missing_vars.is_empty() {
143 String::new()
144 } else if missing_vars.len() == 1 {
145 format!("because variable {} not found", missing_vars[0])
146 } else {
147 format!(
148 "because following variables not found:\n{}\n",
149 missing_vars.join(", ")
150 )
151 };
152
153 (missing_vars.is_empty(), ignore_msg)
154}
155
156#[cfg(not(feature = "runtime"))]
169#[proc_macro_attribute]
170#[proc_macro_error]
171pub fn runtime_env(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
172 panic!("should be used with runtime feature")
173}
174#[cfg(feature = "runtime")]
175#[proc_macro_attribute]
176#[proc_macro_error]
177pub fn runtime_env(attr: TokenStream, stream: TokenStream) -> TokenStream {
178 let attr_str = attr.to_string().replace(' ', "");
179 let var_names: Vec<&str> = attr_str.split(',').collect();
180 let ItemFn {
181 attrs,
182 vis,
183 sig,
184 block,
185 } = parse_macro_input!(stream as ItemFn);
186 let syn::Signature { ident, .. } = sig.clone();
187 let check_ident = syn::Ident::new(
188 &format!("_check_{}", ident.to_string()),
189 proc_macro2::Span::call_site(),
190 );
191 quote::quote! {
192 fn #check_ident() -> Result<(), libtest_with::Failed> {
193 let mut missing_vars = vec![];
194 #(
195 if std::env::var(#var_names).is_err() {
196 missing_vars.push(#var_names);
197 }
198 )*
199 match missing_vars.len() {
200 0 => {
201 #ident();
202 Ok(())
203 },
204 1 => Err(
205 format!("{}because variable {} not found",
206 libtest_with::RUNTIME_IGNORE_PREFIX, missing_vars[0]
207 ).into()),
208 _ => Err(
209 format!("{}because following variables not found:\n{}\n",
210 libtest_with::RUNTIME_IGNORE_PREFIX, missing_vars.join(", ")
211 ).into()),
212 }
213 }
214
215 #(#attrs)*
216 #vis #sig #block
217 }
218 .into()
219}
220
221#[proc_macro_attribute]
234#[proc_macro_error]
235pub fn no_env(attr: TokenStream, stream: TokenStream) -> TokenStream {
236 if is_module(&stream) {
237 mod_macro(
238 attr,
239 parse_macro_input!(stream as ItemMod),
240 check_no_env_condition,
241 )
242 } else {
243 fn_macro(
244 attr,
245 parse_macro_input!(stream as ItemFn),
246 check_no_env_condition,
247 )
248 }
249}
250
251fn check_no_env_condition(attr_str: String) -> (bool, String) {
252 let var_names = sanitize_env_vars_attr(&attr_str);
253
254 let mut found_vars = vec![];
256 for name in var_names {
257 if std::env::var(name).is_ok() {
258 found_vars.push(name.to_string());
259 }
260 }
261
262 let ignore_msg = if found_vars.is_empty() {
264 String::new()
265 } else if found_vars.len() == 1 {
266 format!("because variable {} was found", found_vars[0])
267 } else {
268 format!(
269 "because following variables were found:\n{}\n",
270 found_vars.join(", ")
271 )
272 };
273
274 (found_vars.is_empty(), ignore_msg)
275}
276
277#[cfg(not(feature = "runtime"))]
290#[proc_macro_attribute]
291#[proc_macro_error]
292pub fn runtime_no_env(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
293 panic!("should be used with runtime feature")
294}
295#[cfg(feature = "runtime")]
296#[proc_macro_attribute]
297#[proc_macro_error]
298pub fn runtime_no_env(attr: TokenStream, stream: TokenStream) -> TokenStream {
299 let attr_str = attr.to_string().replace(' ', "");
300 let var_names: Vec<&str> = attr_str.split(',').collect();
301 let ItemFn {
302 attrs,
303 vis,
304 sig,
305 block,
306 } = parse_macro_input!(stream as ItemFn);
307 let syn::Signature { ident, .. } = sig.clone();
308 let check_ident = syn::Ident::new(
309 &format!("_check_{}", ident.to_string()),
310 proc_macro2::Span::call_site(),
311 );
312 quote::quote! {
313 fn #check_ident() -> Result<(), libtest_with::Failed> {
314 let mut should_no_exist_vars = vec![];
315 #(
316 if std::env::var(#var_names).is_ok() {
317 should_no_exist_vars.push(#var_names);
318 }
319 )*
320 match should_no_exist_vars.len() {
321 0 => {
322 #ident();
323 Ok(())
324 },
325 1 => Err(
326 format!("{}because variable {} found",
327 libtest_with::RUNTIME_IGNORE_PREFIX, should_no_exist_vars[0]
328 ).into()),
329 _ => Err(
330 format!("{}because following variables found:\n{}\n",
331 libtest_with::RUNTIME_IGNORE_PREFIX, should_no_exist_vars.join(", ")
332 ).into()),
333 }
334 }
335
336 #(#attrs)*
337 #vis #sig #block
338 }
339 .into()
340}
341
342#[proc_macro_attribute]
370#[proc_macro_error]
371pub fn file(attr: TokenStream, stream: TokenStream) -> TokenStream {
372 if is_module(&stream) {
373 mod_macro(
374 attr,
375 parse_macro_input!(stream as ItemMod),
376 check_file_condition,
377 )
378 } else {
379 fn_macro(
380 attr,
381 parse_macro_input!(stream as ItemFn),
382 check_file_condition,
383 )
384 }
385}
386
387fn check_file_condition(attr_str: String) -> (bool, String) {
388 let files: Vec<&str> = attr_str.split(',').collect();
389 let mut missing_files = vec![];
390 for file in files.iter() {
391 if !Path::new(file.trim_matches('"')).is_file() {
392 missing_files.push(file.to_string());
393 }
394 }
395 let ignore_msg = if missing_files.len() == 1 {
396 format!("because file not found: {}", missing_files[0])
397 } else {
398 format!(
399 "because following files not found: \n{}\n",
400 missing_files.join("\n")
401 )
402 };
403 (missing_files.is_empty(), ignore_msg)
404}
405
406#[cfg(not(feature = "runtime"))]
419#[proc_macro_attribute]
420#[proc_macro_error]
421pub fn runtime_file(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
422 panic!("should be used with runtime feature")
423}
424#[cfg(feature = "runtime")]
425#[proc_macro_attribute]
426#[proc_macro_error]
427pub fn runtime_file(attr: TokenStream, stream: TokenStream) -> TokenStream {
428 let attr_str = attr.to_string().replace(' ', "");
429 let files: Vec<&str> = attr_str.split(',').collect();
430 let ItemFn {
431 attrs,
432 vis,
433 sig,
434 block,
435 } = parse_macro_input!(stream as ItemFn);
436 let syn::Signature { ident, .. } = sig.clone();
437 let check_ident = syn::Ident::new(
438 &format!("_check_{}", ident.to_string()),
439 proc_macro2::Span::call_site(),
440 );
441 quote::quote! {
442 fn #check_ident() -> Result<(), libtest_with::Failed> {
443 let mut missing_files = vec![];
444 #(
445 if !std::path::Path::new(#files.trim_matches('"')).is_file() {
446 missing_files.push(#files);
447 }
448 )*
449
450 match missing_files.len() {
451 0 => {
452 #ident();
453 Ok(())
454 },
455 1 => Err(
456 format!("{}because file not found: {}",
457 libtest_with::RUNTIME_IGNORE_PREFIX, missing_files[0]
458 ).into()),
459 _ => Err(
460 format!("{}because following files not found: \n{}\n",
461 libtest_with::RUNTIME_IGNORE_PREFIX, missing_files.join(", ")
462 ).into()),
463 }
464 }
465
466 #(#attrs)*
467 #vis #sig #block
468 }
469 .into()
470}
471
472#[proc_macro_attribute]
500#[proc_macro_error]
501pub fn path(attr: TokenStream, stream: TokenStream) -> TokenStream {
502 if is_module(&stream) {
503 mod_macro(
504 attr,
505 parse_macro_input!(stream as ItemMod),
506 check_path_condition,
507 )
508 } else {
509 fn_macro(
510 attr,
511 parse_macro_input!(stream as ItemFn),
512 check_path_condition,
513 )
514 }
515}
516
517fn check_path_condition(attr_str: String) -> (bool, String) {
518 let paths: Vec<&str> = attr_str.split(',').collect();
519 let mut missing_paths = vec![];
520 for path in paths.iter() {
521 if metadata(path.trim_matches('"')).is_err() {
522 missing_paths.push(path.to_string());
523 }
524 }
525 let ignore_msg = if missing_paths.len() == 1 {
526 format!("because path not found: {}", missing_paths[0])
527 } else {
528 format!(
529 "because following paths not found: \n{}\n",
530 missing_paths.join("\n")
531 )
532 };
533 (missing_paths.is_empty(), ignore_msg)
534}
535
536#[cfg(not(feature = "runtime"))]
549#[proc_macro_attribute]
550#[proc_macro_error]
551pub fn runtime_path(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
552 panic!("should be used with runtime feature")
553}
554#[cfg(feature = "runtime")]
555#[proc_macro_attribute]
556#[proc_macro_error]
557pub fn runtime_path(attr: TokenStream, stream: TokenStream) -> TokenStream {
558 let attr_str = attr.to_string().replace(' ', "");
559 let paths: Vec<&str> = attr_str.split(',').collect();
560 let ItemFn {
561 attrs,
562 vis,
563 sig,
564 block,
565 } = parse_macro_input!(stream as ItemFn);
566 let syn::Signature { ident, .. } = sig.clone();
567 let check_ident = syn::Ident::new(
568 &format!("_check_{}", ident.to_string()),
569 proc_macro2::Span::call_site(),
570 );
571 quote::quote! {
572 fn #check_ident() -> Result<(), libtest_with::Failed> {
573 let mut missing_paths = vec![];
574 #(
575 if std::fs::metadata(#paths.trim_matches('"')).is_err() {
576 missing_paths.push(#paths.to_string());
577 }
578 )*
579
580 match missing_paths.len() {
581 0 => {
582 #ident();
583 Ok(())
584 },
585 1 => Err(
586 format!("{}because path not found: {}",
587 libtest_with::RUNTIME_IGNORE_PREFIX, missing_paths[0]
588 ).into()),
589 _ => Err(
590 format!("{}because following paths not found: \n{}\n",
591 libtest_with::RUNTIME_IGNORE_PREFIX, missing_paths.join(", ")
592 ).into()),
593 }
594 }
595
596 #(#attrs)*
597 #vis #sig #block
598 }
599 .into()
600}
601
602#[proc_macro_attribute]
623#[proc_macro_error]
624#[cfg(feature = "http")]
625pub fn http(attr: TokenStream, stream: TokenStream) -> TokenStream {
626 if is_module(&stream) {
627 mod_macro(
628 attr,
629 parse_macro_input!(stream as ItemMod),
630 check_http_condition,
631 )
632 } else {
633 fn_macro(
634 attr,
635 parse_macro_input!(stream as ItemFn),
636 check_http_condition,
637 )
638 }
639}
640
641#[cfg(feature = "http")]
642fn check_http_condition(attr_str: String) -> (bool, String) {
643 let links: Vec<&str> = attr_str.split(',').collect();
644 let mut missing_links = vec![];
645 let client = reqwest::blocking::Client::new();
646 for link in links.iter() {
647 if client.head(&format!("http://{}", link)).send().is_err() {
648 missing_links.push(format!("http://{link:}"));
649 }
650 }
651 let ignore_msg = if missing_links.len() == 1 {
652 format!("because {} not response", missing_links[0])
653 } else {
654 format!(
655 "because following links not response: \n{}\n",
656 missing_links.join("\n")
657 )
658 };
659 (missing_links.is_empty(), ignore_msg)
660}
661
662#[cfg(not(feature = "runtime"))]
674#[proc_macro_attribute]
675#[proc_macro_error]
676pub fn runtime_http(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
677 panic!("should be used with runtime feature")
678}
679
680#[cfg(all(feature = "runtime", feature = "http"))]
681#[proc_macro_attribute]
682#[proc_macro_error]
683pub fn runtime_http(attr: TokenStream, stream: TokenStream) -> TokenStream {
684 let attr_str = attr.to_string().replace(' ', "");
685 let links: Vec<&str> = attr_str.split(',').collect();
686 let ItemFn {
687 attrs,
688 vis,
689 sig,
690 block,
691 } = parse_macro_input!(stream as ItemFn);
692 let syn::Signature { ident, .. } = sig.clone();
693 let check_ident = syn::Ident::new(
694 &format!("_check_{}", ident.to_string()),
695 proc_macro2::Span::call_site(),
696 );
697 quote::quote! {
698 fn #check_ident() -> Result<(), libtest_with::Failed> {
699
700 let mut missing_links = vec![];
701 let client = libtest_with::reqwest::blocking::Client::new();
702 #(
703 if client.head(&format!("http://{}", #links)).send().is_err() {
704 missing_links.push(format!("http://{}", #links));
705 }
706 )*
707 match missing_links.len() {
708 0 => {
709 #ident();
710 Ok(())
711 },
712 1 => Err(
713 format!("{}because {} not response",
714 libtest_with::RUNTIME_IGNORE_PREFIX, missing_links[0]
715 ).into()),
716 _ => Err(
717 format!("{}because following links not response: \n{}\n",
718 libtest_with::RUNTIME_IGNORE_PREFIX, missing_links.join(", ")
719 ).into()),
720 }
721 }
722
723 #(#attrs)*
724 #vis #sig #block
725 }
726 .into()
727}
728
729#[proc_macro_attribute]
750#[proc_macro_error]
751#[cfg(feature = "http")]
752pub fn https(attr: TokenStream, stream: TokenStream) -> TokenStream {
753 if is_module(&stream) {
754 mod_macro(
755 attr,
756 parse_macro_input!(stream as ItemMod),
757 check_https_condition,
758 )
759 } else {
760 fn_macro(
761 attr,
762 parse_macro_input!(stream as ItemFn),
763 check_https_condition,
764 )
765 }
766}
767
768#[cfg(feature = "http")]
769fn check_https_condition(attr_str: String) -> (bool, String) {
770 let links: Vec<&str> = attr_str.split(',').collect();
771 let mut missing_links = vec![];
772 let client = reqwest::blocking::Client::new();
773 for link in links.iter() {
774 if client.head(&format!("https://{}", link)).send().is_err() {
775 missing_links.push(format!("https://{link:}"));
776 }
777 }
778 let ignore_msg = if missing_links.len() == 1 {
779 format!("because {} not response", missing_links[0])
780 } else {
781 format!(
782 "because following links not response: \n{}\n",
783 missing_links.join("\n")
784 )
785 };
786 (missing_links.is_empty(), ignore_msg)
787}
788
789#[cfg(not(feature = "runtime"))]
801#[proc_macro_attribute]
802#[proc_macro_error]
803pub fn runtime_https(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
804 panic!("should be used with runtime feature")
805}
806
807#[cfg(all(feature = "runtime", feature = "http"))]
808#[proc_macro_attribute]
809#[proc_macro_error]
810pub fn runtime_https(attr: TokenStream, stream: TokenStream) -> TokenStream {
811 let attr_str = attr.to_string().replace(' ', "");
812 let links: Vec<&str> = attr_str.split(',').collect();
813 let ItemFn {
814 attrs,
815 vis,
816 sig,
817 block,
818 } = parse_macro_input!(stream as ItemFn);
819 let syn::Signature { ident, .. } = sig.clone();
820 let check_ident = syn::Ident::new(
821 &format!("_check_{}", ident.to_string()),
822 proc_macro2::Span::call_site(),
823 );
824 quote::quote! {
825 fn #check_ident() -> Result<(), libtest_with::Failed> {
826
827 let mut missing_links = vec![];
828 let client = libtest_with::reqwest::blocking::Client::new();
829 #(
830 if client.head(&format!("https://{}", #links)).send().is_err() {
831 missing_links.push(format!("https://{}", #links));
832 }
833 )*
834 match missing_links.len() {
835 0 => {
836 #ident();
837 Ok(())
838 },
839 1 => Err(
840 format!("{}because {} not response",
841 libtest_with::RUNTIME_IGNORE_PREFIX, missing_links[0]
842 ).into()),
843 _ => Err(
844 format!("{}because following links not response: \n{}\n",
845 libtest_with::RUNTIME_IGNORE_PREFIX, missing_links.join(", ")
846 ).into()),
847 }
848 }
849
850 #(#attrs)*
851 #vis #sig #block
852 }
853 .into()
854}
855
856#[proc_macro_attribute]
879#[proc_macro_error]
880#[cfg(feature = "icmp")]
881pub fn icmp(attr: TokenStream, stream: TokenStream) -> TokenStream {
882 if is_module(&stream) {
883 mod_macro(
884 attr,
885 parse_macro_input!(stream as ItemMod),
886 check_icmp_condition,
887 )
888 } else {
889 fn_macro(
890 attr,
891 parse_macro_input!(stream as ItemFn),
892 check_icmp_condition,
893 )
894 }
895}
896
897#[cfg(feature = "icmp")]
898fn check_icmp_condition(attr_str: String) -> (bool, String) {
899 let ips: Vec<&str> = attr_str.split(',').collect();
900 let mut missing_ips = vec![];
901 for ip in ips.iter() {
902 if let Ok(addr) = ip.parse::<IpAddr>() {
903 if ping::ping(addr, None, None, None, None, None).is_err() {
904 missing_ips.push(ip.to_string());
905 }
906 } else {
907 abort_call_site!("ip address malformat")
908 }
909 }
910 let ignore_msg = if missing_ips.len() == 1 {
911 format!("because ip {} not response", missing_ips[0])
912 } else {
913 format!(
914 "because following ip not response: \n{}\n",
915 missing_ips.join(", ")
916 )
917 };
918 (missing_ips.is_empty(), ignore_msg)
919}
920
921#[cfg(not(feature = "runtime"))]
935#[proc_macro_attribute]
936#[proc_macro_error]
937pub fn runtime_icmp(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
938 panic!("should be used with runtime feature")
939}
940
941#[cfg(all(feature = "runtime", feature = "icmp"))]
942#[proc_macro_attribute]
943#[proc_macro_error]
944pub fn runtime_icmp(attr: TokenStream, stream: TokenStream) -> TokenStream {
945 let attr_str = attr.to_string().replace(' ', "");
946 let ips: Vec<&str> = attr_str.split(',').collect();
947 let ItemFn {
948 attrs,
949 vis,
950 sig,
951 block,
952 } = parse_macro_input!(stream as ItemFn);
953 let syn::Signature { ident, .. } = sig.clone();
954 let check_ident = syn::Ident::new(
955 &format!("_check_{}", ident.to_string()),
956 proc_macro2::Span::call_site(),
957 );
958 quote::quote! {
959 fn #check_ident() -> Result<(), libtest_with::Failed> {
960
961 let mut missing_ips = vec![];
962 #(
963 if libtest_with::ping::ping(#ips.parse().expect("ip address is invalid"), None, None, None, None, None).is_err() {
964 missing_ips.push(#ips);
965 }
966 )*
967 match missing_ips.len() {
968 0 => {
969 #ident();
970 Ok(())
971 }
972 ,
973 1 => Err(
974 format!("{}because {} not response",
975 libtest_with::RUNTIME_IGNORE_PREFIX, missing_ips[0]
976 ).into()),
977 _ => Err(
978 format!("{}because following ips not response: \n{}\n",
979 libtest_with::RUNTIME_IGNORE_PREFIX, missing_ips.join(", ")
980 ).into()),
981 }
982 }
983
984 #(#attrs)*
985 #vis #sig #block
986 }
987 .into()
988}
989
990#[proc_macro_attribute]
1012#[proc_macro_error]
1013pub fn tcp(attr: TokenStream, stream: TokenStream) -> TokenStream {
1014 if is_module(&stream) {
1015 mod_macro(
1016 attr,
1017 parse_macro_input!(stream as ItemMod),
1018 check_tcp_condition,
1019 )
1020 } else {
1021 fn_macro(
1022 attr,
1023 parse_macro_input!(stream as ItemFn),
1024 check_tcp_condition,
1025 )
1026 }
1027}
1028
1029fn check_tcp_condition(attr_str: String) -> (bool, String) {
1030 let sockets: Vec<&str> = attr_str.split(',').collect();
1031 let mut missing_sockets = vec![];
1032 for socket in sockets.iter() {
1033 if TcpStream::connect(socket).is_err() {
1034 missing_sockets.push(socket.to_string());
1035 }
1036 }
1037 let ignore_msg = if missing_sockets.len() == 1 {
1038 format!("because fail to connect socket {}", missing_sockets[0])
1039 } else {
1040 format!(
1041 "because follow sockets can not connect\n{}\n",
1042 missing_sockets.join(", ")
1043 )
1044 };
1045 (missing_sockets.is_empty(), ignore_msg)
1046}
1047
1048#[cfg(not(feature = "runtime"))]
1061#[proc_macro_attribute]
1062#[proc_macro_error]
1063pub fn runtime_tcp(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1064 panic!("should be used with runtime feature")
1065}
1066
1067#[cfg(feature = "runtime")]
1068#[proc_macro_attribute]
1069#[proc_macro_error]
1070pub fn runtime_tcp(attr: TokenStream, stream: TokenStream) -> TokenStream {
1071 let attr_str = attr.to_string().replace(' ', "");
1072 let sockets: Vec<&str> = attr_str.split(',').collect();
1073 let ItemFn {
1074 attrs,
1075 vis,
1076 sig,
1077 block,
1078 } = parse_macro_input!(stream as ItemFn);
1079 let syn::Signature { ident, .. } = sig.clone();
1080 let check_ident = syn::Ident::new(
1081 &format!("_check_{}", ident.to_string()),
1082 proc_macro2::Span::call_site(),
1083 );
1084 quote::quote! {
1085 fn #check_ident() -> Result<(), libtest_with::Failed> {
1086
1087 let mut missing_sockets = vec![];
1088 #(
1089 if std::net::TcpStream::connect(#sockets).is_err() {
1090 missing_sockets.push(#sockets);
1091 }
1092 )*
1093 match missing_sockets.len() {
1094 0 => {
1095 #ident();
1096 Ok(())
1097 },
1098 1 => Err(
1099 format!("{}because {} not response",
1100 libtest_with::RUNTIME_IGNORE_PREFIX, missing_sockets[0]
1101 ).into()),
1102 _ => Err(
1103 format!("{}because following sockets not response: \n{}\n",
1104 libtest_with::RUNTIME_IGNORE_PREFIX, missing_sockets.join(", ")
1105 ).into()),
1106 }
1107 }
1108
1109 #(#attrs)*
1110 #vis #sig #block
1111 }
1112 .into()
1113}
1114
1115#[proc_macro_attribute]
1130#[proc_macro_error]
1131#[cfg(all(feature = "user", not(target_os = "windows")))]
1132pub fn root(attr: TokenStream, stream: TokenStream) -> TokenStream {
1133 if is_module(&stream) {
1134 mod_macro(
1135 attr,
1136 parse_macro_input!(stream as ItemMod),
1137 check_root_condition,
1138 )
1139 } else {
1140 fn_macro(
1141 attr,
1142 parse_macro_input!(stream as ItemFn),
1143 check_root_condition,
1144 )
1145 }
1146}
1147
1148#[cfg(all(feature = "user", not(target_os = "windows")))]
1149fn check_root_condition(_attr_str: String) -> (bool, String) {
1150 let current_user_id = uzers::get_current_uid();
1151 (
1152 current_user_id == 0,
1153 "because this case should run with root".into(),
1154 )
1155}
1156
1157#[cfg(not(feature = "runtime"))]
1170#[proc_macro_attribute]
1171#[proc_macro_error]
1172pub fn runtime_root(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1173 panic!("should be used with runtime feature")
1174}
1175#[cfg(all(feature = "runtime", feature = "user", not(target_os = "windows")))]
1176#[proc_macro_attribute]
1177#[proc_macro_error]
1178pub fn runtime_root(_attr: TokenStream, stream: TokenStream) -> TokenStream {
1179 let ItemFn {
1180 attrs,
1181 vis,
1182 sig,
1183 block,
1184 } = parse_macro_input!(stream as ItemFn);
1185 let syn::Signature { ident, .. } = sig.clone();
1186 let check_ident = syn::Ident::new(
1187 &format!("_check_{}", ident.to_string()),
1188 proc_macro2::Span::call_site(),
1189 );
1190 quote::quote! {
1191 fn #check_ident() -> Result<(), libtest_with::Failed> {
1192 if 0 == libtest_with::uzers::get_current_uid() {
1193 #ident();
1194 Ok(())
1195 } else {
1196 Err(format!("{}because this case should run with root", libtest_with::RUNTIME_IGNORE_PREFIX).into())
1197 }
1198 }
1199
1200 #(#attrs)*
1201 #vis #sig #block
1202 }
1203 .into()
1204}
1205
1206#[proc_macro_attribute]
1221#[proc_macro_error]
1222#[cfg(all(feature = "user", not(target_os = "windows")))]
1223pub fn group(attr: TokenStream, stream: TokenStream) -> TokenStream {
1224 if is_module(&stream) {
1225 mod_macro(
1226 attr,
1227 parse_macro_input!(stream as ItemMod),
1228 check_group_condition,
1229 )
1230 } else {
1231 fn_macro(
1232 attr,
1233 parse_macro_input!(stream as ItemFn),
1234 check_group_condition,
1235 )
1236 }
1237}
1238
1239#[cfg(feature = "user")]
1240#[cfg(all(feature = "user", not(target_os = "windows")))]
1241fn check_group_condition(group_name: String) -> (bool, String) {
1242 let current_user_id = uzers::get_current_uid();
1243
1244 let in_group = match uzers::get_user_by_uid(current_user_id) {
1245 Some(user) => {
1246 let mut in_group = false;
1247 for group in user.groups().expect("user not found") {
1248 if in_group {
1249 break;
1250 }
1251 in_group |= group.name().to_string_lossy() == group_name;
1252 }
1253 in_group
1254 }
1255 None => false,
1256 };
1257 (
1258 in_group,
1259 format!("because this case should run user in group {}", group_name),
1260 )
1261}
1262
1263#[cfg(not(feature = "runtime"))]
1276#[proc_macro_attribute]
1277#[proc_macro_error]
1278pub fn runtime_group(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1279 panic!("should be used with runtime feature")
1280}
1281#[cfg(all(feature = "runtime", feature = "user", not(target_os = "windows")))]
1282#[proc_macro_attribute]
1283#[proc_macro_error]
1284pub fn runtime_group(attr: TokenStream, stream: TokenStream) -> TokenStream {
1285 let group_name = attr.to_string().replace(' ', "");
1286 let ItemFn {
1287 attrs,
1288 vis,
1289 sig,
1290 block,
1291 } = parse_macro_input!(stream as ItemFn);
1292 let syn::Signature { ident, .. } = sig.clone();
1293 let check_ident = syn::Ident::new(
1294 &format!("_check_{}", ident.to_string()),
1295 proc_macro2::Span::call_site(),
1296 );
1297
1298 quote::quote! {
1299
1300 fn #check_ident() -> Result<(), libtest_with::Failed> {
1301 let current_user_id = libtest_with::uzers::get_current_uid();
1302 let in_group = match libtest_with::uzers::get_user_by_uid(current_user_id) {
1303 Some(user) => {
1304 let mut in_group = false;
1305 for group in user.groups().expect("user not found") {
1306 if in_group {
1307 break;
1308 }
1309 in_group |= group.name().to_string_lossy() == #group_name;
1310 }
1311 in_group
1312 }
1313 None => false,
1314 };
1315
1316 if in_group {
1317 #ident();
1318 Ok(())
1319 } else {
1320 Err(format!("{}because this case should run user in group {}",
1321 libtest_with::RUNTIME_IGNORE_PREFIX, #group_name).into())
1322 }
1323 }
1324
1325 #(#attrs)*
1326 #vis #sig #block
1327 }
1328 .into()
1329}
1330
1331#[proc_macro_attribute]
1346#[proc_macro_error]
1347#[cfg(all(feature = "user", not(target_os = "windows")))]
1348pub fn user(attr: TokenStream, stream: TokenStream) -> TokenStream {
1349 if is_module(&stream) {
1350 mod_macro(
1351 attr,
1352 parse_macro_input!(stream as ItemMod),
1353 check_user_condition,
1354 )
1355 } else {
1356 fn_macro(
1357 attr,
1358 parse_macro_input!(stream as ItemFn),
1359 check_user_condition,
1360 )
1361 }
1362}
1363
1364#[cfg(feature = "user")]
1365#[cfg(all(feature = "user", not(target_os = "windows")))]
1366fn check_user_condition(user_name: String) -> (bool, String) {
1367 let is_user = match uzers::get_current_username() {
1368 Some(uname) => uname.to_string_lossy() == user_name,
1369 None => false,
1370 };
1371 (
1372 is_user,
1373 format!("because this case should run with user {}", user_name),
1374 )
1375}
1376
1377#[cfg(not(feature = "runtime"))]
1390#[proc_macro_attribute]
1391#[proc_macro_error]
1392pub fn runtime_user(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1393 panic!("should be used with runtime feature")
1394}
1395#[cfg(all(feature = "runtime", feature = "user", not(target_os = "windows")))]
1396#[proc_macro_attribute]
1397#[proc_macro_error]
1398pub fn runtime_user(attr: TokenStream, stream: TokenStream) -> TokenStream {
1399 let user_name = attr.to_string().replace(' ', "");
1400 let ItemFn {
1401 attrs,
1402 vis,
1403 sig,
1404 block,
1405 } = parse_macro_input!(stream as ItemFn);
1406 let syn::Signature { ident, .. } = sig.clone();
1407 let check_ident = syn::Ident::new(
1408 &format!("_check_{}", ident.to_string()),
1409 proc_macro2::Span::call_site(),
1410 );
1411
1412 quote::quote! {
1413
1414 fn #check_ident() -> Result<(), libtest_with::Failed> {
1415 let is_user = match libtest_with::uzers::get_current_username() {
1416 Some(uname) => uname.to_string_lossy() == #user_name,
1417 None => false,
1418 };
1419
1420 if is_user {
1421 #ident();
1422 Ok(())
1423 } else {
1424 Err(format!("{}because this case should run with user {}",
1425 libtest_with::RUNTIME_IGNORE_PREFIX, #user_name).into())
1426 }
1427 }
1428
1429 #(#attrs)*
1430 #vis #sig #block
1431 }
1432 .into()
1433}
1434
1435#[proc_macro_attribute]
1450#[proc_macro_error]
1451#[cfg(feature = "resource")]
1452pub fn mem(attr: TokenStream, stream: TokenStream) -> TokenStream {
1453 if is_module(&stream) {
1454 mod_macro(
1455 attr,
1456 parse_macro_input!(stream as ItemMod),
1457 check_mem_condition,
1458 )
1459 } else {
1460 fn_macro(
1461 attr,
1462 parse_macro_input!(stream as ItemFn),
1463 check_mem_condition,
1464 )
1465 }
1466}
1467
1468#[cfg(feature = "resource")]
1469fn check_mem_condition(mem_size_str: String) -> (bool, String) {
1470 let sys = sysinfo::System::new_with_specifics(
1471 sysinfo::RefreshKind::nothing()
1472 .with_memory(sysinfo::MemoryRefreshKind::nothing().with_swap()),
1473 );
1474 let mem_size = match byte_unit::Byte::parse_str(format!("{} B", sys.total_memory()), false) {
1475 Ok(b) => b,
1476 Err(_) => abort_call_site!("memory size description is not correct"),
1477 };
1478 let mem_size_limitation = match byte_unit::Byte::parse_str(&mem_size_str, true) {
1479 Ok(b) => b,
1480 Err(_) => abort_call_site!("system memory size can not get"),
1481 };
1482 (
1483 mem_size >= mem_size_limitation,
1484 format!("because the memory less than {}", mem_size_str),
1485 )
1486}
1487
1488#[cfg(not(feature = "runtime"))]
1501#[proc_macro_attribute]
1502#[proc_macro_error]
1503pub fn runtime_mem(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1504 panic!("should be used with runtime feature")
1505}
1506#[cfg(all(feature = "runtime", feature = "resource"))]
1507#[proc_macro_attribute]
1508#[proc_macro_error]
1509pub fn runtime_mem(attr: TokenStream, stream: TokenStream) -> TokenStream {
1510 let mem_limitation_str = attr.to_string().replace(' ', "");
1511 if byte_unit::Byte::parse_str(&mem_limitation_str, true).is_err() {
1512 abort_call_site!("memory size description is not correct")
1513 }
1514
1515 let ItemFn {
1516 attrs,
1517 vis,
1518 sig,
1519 block,
1520 } = parse_macro_input!(stream as ItemFn);
1521 let syn::Signature { ident, .. } = sig.clone();
1522 let check_ident = syn::Ident::new(
1523 &format!("_check_{}", ident.to_string()),
1524 proc_macro2::Span::call_site(),
1525 );
1526
1527 quote::quote! {
1528 fn #check_ident() -> Result<(), libtest_with::Failed> {
1529 let sys = libtest_with::sysinfo::System::new_with_specifics(
1530 libtest_with::sysinfo::RefreshKind::nothing().with_memory(libtest_with::sysinfo::MemoryRefreshKind::nothing().with_ram()),
1531 );
1532 let mem_size = match libtest_with::byte_unit::Byte::parse_str(format!("{} B", sys.total_memory()), false) {
1533 Ok(b) => b,
1534 Err(_) => panic!("system memory size can not get"),
1535 };
1536 let mem_size_limitation = libtest_with::byte_unit::Byte::parse_str(#mem_limitation_str, true).expect("mem limitation should correct");
1537 if mem_size >= mem_size_limitation {
1538 #ident();
1539 Ok(())
1540 } else {
1541 Err(format!("{}because the memory less than {}",
1542 libtest_with::RUNTIME_IGNORE_PREFIX, #mem_limitation_str).into())
1543 }
1544 }
1545
1546 #(#attrs)*
1547 #vis #sig #block
1548
1549 }
1550 .into()
1551}
1552
1553#[cfg(not(feature = "runtime"))]
1566#[proc_macro_attribute]
1567#[proc_macro_error]
1568pub fn runtime_free_mem(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1569 panic!("should be used with runtime feature")
1570}
1571#[cfg(all(feature = "runtime", feature = "resource"))]
1572#[proc_macro_attribute]
1573#[proc_macro_error]
1574pub fn runtime_free_mem(attr: TokenStream, stream: TokenStream) -> TokenStream {
1575 let mem_limitation_str = attr.to_string().replace(' ', "");
1576 if byte_unit::Byte::parse_str(&mem_limitation_str, true).is_err() {
1577 abort_call_site!("memory size description is not correct")
1578 }
1579
1580 let ItemFn {
1581 attrs,
1582 vis,
1583 sig,
1584 block,
1585 } = parse_macro_input!(stream as ItemFn);
1586 let syn::Signature { ident, .. } = sig.clone();
1587 let check_ident = syn::Ident::new(
1588 &format!("_check_{}", ident.to_string()),
1589 proc_macro2::Span::call_site(),
1590 );
1591
1592 quote::quote! {
1593 fn #check_ident() -> Result<(), libtest_with::Failed> {
1594 let sys = libtest_with::sysinfo::System::new_with_specifics(
1595 libtest_with::sysinfo::RefreshKind::nothing().with_memory(libtest_with::sysinfo::MemoryRefreshKind::nothing().with_ram()),
1596 );
1597 let mem_size = match libtest_with::byte_unit::Byte::parse_str(format!("{} B", sys.free_memory()), false) {
1598 Ok(b) => b,
1599 Err(_) => panic!("system memory size can not get"),
1600 };
1601 let mem_size_limitation = libtest_with::byte_unit::Byte::parse_str(#mem_limitation_str, true).expect("mem limitation should correct");
1602 if mem_size >= mem_size_limitation {
1603 #ident();
1604 Ok(())
1605 } else {
1606 Err(format!("{}because the memory less than {}",
1607 libtest_with::RUNTIME_IGNORE_PREFIX, #mem_limitation_str).into())
1608 }
1609 }
1610
1611 #(#attrs)*
1612 #vis #sig #block
1613
1614 }
1615 .into()
1616}
1617
1618#[cfg(not(feature = "runtime"))]
1631#[proc_macro_attribute]
1632#[proc_macro_error]
1633pub fn runtime_available_mem(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1634 panic!("should be used with runtime feature")
1635}
1636#[cfg(all(feature = "runtime", feature = "resource"))]
1637#[proc_macro_attribute]
1638#[proc_macro_error]
1639pub fn runtime_available_mem(attr: TokenStream, stream: TokenStream) -> TokenStream {
1640 let mem_limitation_str = attr.to_string().replace(' ', "");
1641 if byte_unit::Byte::parse_str(&mem_limitation_str, true).is_err() {
1642 abort_call_site!("memory size description is not correct")
1643 }
1644
1645 let ItemFn {
1646 attrs,
1647 vis,
1648 sig,
1649 block,
1650 } = parse_macro_input!(stream as ItemFn);
1651 let syn::Signature { ident, .. } = sig.clone();
1652 let check_ident = syn::Ident::new(
1653 &format!("_check_{}", ident.to_string()),
1654 proc_macro2::Span::call_site(),
1655 );
1656
1657 quote::quote! {
1658 fn #check_ident() -> Result<(), libtest_with::Failed> {
1659 let sys = libtest_with::sysinfo::System::new_with_specifics(
1660 libtest_with::sysinfo::RefreshKind::nothing().with_memory(libtest_with::sysinfo::MemoryRefreshKind::nothing().with_ram()),
1661 );
1662 let mem_size = match libtest_with::byte_unit::Byte::parse_str(format!("{} B", sys.available_memory()), false) {
1663 Ok(b) => b,
1664 Err(_) => panic!("system memory size can not get"),
1665 };
1666 let mem_size_limitation = libtest_with::byte_unit::Byte::parse_str(#mem_limitation_str, true).expect("mem limitation should correct");
1667 if mem_size >= mem_size_limitation {
1668 #ident();
1669 Ok(())
1670 } else {
1671 Err(format!("{}because the memory less than {}",
1672 libtest_with::RUNTIME_IGNORE_PREFIX, #mem_limitation_str).into())
1673 }
1674 }
1675
1676 #(#attrs)*
1677 #vis #sig #block
1678
1679 }
1680 .into()
1681}
1682
1683#[proc_macro_attribute]
1698#[proc_macro_error]
1699#[cfg(feature = "resource")]
1700pub fn swap(attr: TokenStream, stream: TokenStream) -> TokenStream {
1701 if is_module(&stream) {
1702 mod_macro(
1703 attr,
1704 parse_macro_input!(stream as ItemMod),
1705 check_swap_condition,
1706 )
1707 } else {
1708 fn_macro(
1709 attr,
1710 parse_macro_input!(stream as ItemFn),
1711 check_swap_condition,
1712 )
1713 }
1714}
1715
1716#[cfg(feature = "resource")]
1717fn check_swap_condition(swap_size_str: String) -> (bool, String) {
1718 let sys = sysinfo::System::new_with_specifics(
1719 sysinfo::RefreshKind::nothing()
1720 .with_memory(sysinfo::MemoryRefreshKind::nothing().with_swap()),
1721 );
1722 let swap_size = match byte_unit::Byte::parse_str(format!("{} B", sys.total_swap()), false) {
1723 Ok(b) => b,
1724 Err(_) => abort_call_site!("Swap size description is not correct"),
1725 };
1726 let swap_size_limitation = match byte_unit::Byte::parse_str(&swap_size_str, true) {
1727 Ok(b) => b,
1728 Err(_) => abort_call_site!("Can not get system swap size"),
1729 };
1730 (
1731 swap_size >= swap_size_limitation,
1732 format!("because the swap less than {}", swap_size_str),
1733 )
1734}
1735
1736#[cfg(not(feature = "runtime"))]
1749#[proc_macro_attribute]
1750#[proc_macro_error]
1751pub fn runtime_swap(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1752 panic!("should be used with runtime feature")
1753}
1754#[cfg(all(feature = "runtime", feature = "resource"))]
1755#[proc_macro_attribute]
1756#[proc_macro_error]
1757pub fn runtime_swap(attr: TokenStream, stream: TokenStream) -> TokenStream {
1758 let swap_limitation_str = attr.to_string().replace(' ', "");
1759 if byte_unit::Byte::parse_str(&swap_limitation_str, true).is_err() {
1760 abort_call_site!("swap size description is not correct")
1761 }
1762
1763 let ItemFn {
1764 attrs,
1765 vis,
1766 sig,
1767 block,
1768 } = parse_macro_input!(stream as ItemFn);
1769 let syn::Signature { ident, .. } = sig.clone();
1770 let check_ident = syn::Ident::new(
1771 &format!("_check_{}", ident.to_string()),
1772 proc_macro2::Span::call_site(),
1773 );
1774
1775 quote::quote! {
1776 fn #check_ident() -> Result<(), libtest_with::Failed> {
1777 let sys = libtest_with::sysinfo::System::new_with_specifics(
1778 libtest_with::sysinfo::RefreshKind::nothing().with_memory(libtest_with::sysinfo::MemoryRefreshKind::nothing().with_swap()),
1779 );
1780 let swap_size = match libtest_with::byte_unit::Byte::parse_str(format!("{} B", sys.total_swap()), false) {
1781 Ok(b) => b,
1782 Err(_) => panic!("system swap size can not get"),
1783 };
1784 let swap_size_limitation = libtest_with::byte_unit::Byte::parse_str(#swap_limitation_str, true).expect("swap limitation should correct");
1785 if swap_size >= swap_size_limitation {
1786 #ident();
1787 Ok(())
1788 } else {
1789 Err(format!("{}because the swap less than {}",
1790 libtest_with::RUNTIME_IGNORE_PREFIX, #swap_limitation_str).into())
1791 }
1792 }
1793
1794 #(#attrs)*
1795 #vis #sig #block
1796
1797 }
1798 .into()
1799}
1800
1801#[cfg(not(feature = "runtime"))]
1814#[proc_macro_attribute]
1815#[proc_macro_error]
1816pub fn runtime_free_swap(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1817 panic!("should be used with runtime feature")
1818}
1819#[cfg(all(feature = "runtime", feature = "resource"))]
1820#[proc_macro_attribute]
1821#[proc_macro_error]
1822pub fn runtime_free_swap(attr: TokenStream, stream: TokenStream) -> TokenStream {
1823 let swap_limitation_str = attr.to_string().replace(' ', "");
1824 if byte_unit::Byte::parse_str(&swap_limitation_str, true).is_err() {
1825 abort_call_site!("swap size description is not correct")
1826 }
1827
1828 let ItemFn {
1829 attrs,
1830 vis,
1831 sig,
1832 block,
1833 } = parse_macro_input!(stream as ItemFn);
1834 let syn::Signature { ident, .. } = sig.clone();
1835 let check_ident = syn::Ident::new(
1836 &format!("_check_{}", ident.to_string()),
1837 proc_macro2::Span::call_site(),
1838 );
1839
1840 quote::quote! {
1841 fn #check_ident() -> Result<(), libtest_with::Failed> {
1842 let sys = libtest_with::sysinfo::System::new_with_specifics(
1843 libtest_with::sysinfo::RefreshKind::nothing().with_memory(libtest_with::sysinfo::MemoryRefreshKind::nothing().with_swap()),
1844 );
1845 let swap_size = match libtest_with::byte_unit::Byte::parse_str(format!("{} B", sys.free_swap()), false) {
1846 Ok(b) => b,
1847 Err(_) => panic!("system swap size can not get"),
1848 };
1849 let swap_size_limitation = libtest_with::byte_unit::Byte::parse_str(#swap_limitation_str, true).expect("swap limitation should correct");
1850 if swap_size >= swap_size_limitation {
1851 #ident();
1852 Ok(())
1853 } else {
1854 Err(format!("{}because the swap less than {}",
1855 libtest_with::RUNTIME_IGNORE_PREFIX, #swap_limitation_str).into())
1856 }
1857 }
1858
1859 #(#attrs)*
1860 #vis #sig #block
1861
1862 }
1863 .into()
1864}
1865
1866#[proc_macro_attribute]
1881#[proc_macro_error]
1882#[cfg(feature = "resource")]
1883pub fn cpu_core(attr: TokenStream, stream: TokenStream) -> TokenStream {
1884 if is_module(&stream) {
1885 mod_macro(
1886 attr,
1887 parse_macro_input!(stream as ItemMod),
1888 check_cpu_core_condition,
1889 )
1890 } else {
1891 fn_macro(
1892 attr,
1893 parse_macro_input!(stream as ItemFn),
1894 check_cpu_core_condition,
1895 )
1896 }
1897}
1898
1899#[cfg(feature = "resource")]
1900fn check_cpu_core_condition(core_limitation_str: String) -> (bool, String) {
1901 (
1902 match core_limitation_str.parse::<usize>() {
1903 Ok(c) => num_cpus::get() >= c,
1904 Err(_) => abort_call_site!("core limitation is incorrect"),
1905 },
1906 format!("because the cpu core less than {}", core_limitation_str),
1907 )
1908}
1909
1910#[cfg(not(feature = "runtime"))]
1923#[proc_macro_attribute]
1924#[proc_macro_error]
1925pub fn runtime_cpu_core(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1926 panic!("should be used with runtime feature")
1927}
1928#[cfg(all(feature = "runtime", feature = "resource"))]
1929#[proc_macro_attribute]
1930#[proc_macro_error]
1931pub fn runtime_cpu_core(attr: TokenStream, stream: TokenStream) -> TokenStream {
1932 let attr_str = attr.to_string().replace(' ', "");
1933 let core_limitation = match attr_str.parse::<usize>() {
1934 Ok(c) => c,
1935 Err(_) => abort_call_site!("core limitation is incorrect"),
1936 };
1937
1938 let ItemFn {
1939 attrs,
1940 vis,
1941 sig,
1942 block,
1943 } = parse_macro_input!(stream as ItemFn);
1944 let syn::Signature { ident, .. } = sig.clone();
1945 let check_ident = syn::Ident::new(
1946 &format!("_check_{}", ident.to_string()),
1947 proc_macro2::Span::call_site(),
1948 );
1949
1950 quote::quote! {
1951 fn #check_ident() -> Result<(), libtest_with::Failed> {
1952 if libtest_with::num_cpus::get() >= #core_limitation {
1953 #ident();
1954 Ok(())
1955 } else {
1956 Err(format!("{}because the cpu core less than {}",
1957 libtest_with::RUNTIME_IGNORE_PREFIX, #core_limitation).into())
1958 }
1959 }
1960
1961 #(#attrs)*
1962 #vis #sig #block
1963
1964 }
1965 .into()
1966}
1967
1968#[proc_macro_attribute]
1983#[proc_macro_error]
1984#[cfg(feature = "resource")]
1985pub fn phy_core(attr: TokenStream, stream: TokenStream) -> TokenStream {
1986 if is_module(&stream) {
1987 mod_macro(
1988 attr,
1989 parse_macro_input!(stream as ItemMod),
1990 check_cpu_core_condition,
1991 )
1992 } else {
1993 fn_macro(
1994 attr,
1995 parse_macro_input!(stream as ItemFn),
1996 check_phy_core_condition,
1997 )
1998 }
1999}
2000
2001#[cfg(feature = "resource")]
2002fn check_phy_core_condition(core_limitation_str: String) -> (bool, String) {
2003 (
2004 match core_limitation_str.parse::<usize>() {
2005 Ok(c) => num_cpus::get_physical() >= c,
2006 Err(_) => abort_call_site!("physical core limitation is incorrect"),
2007 },
2008 format!(
2009 "because the physical cpu core less than {}",
2010 core_limitation_str
2011 ),
2012 )
2013}
2014
2015#[cfg(not(feature = "runtime"))]
2028#[proc_macro_attribute]
2029#[proc_macro_error]
2030pub fn runtime_phy_cpu_core(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
2031 panic!("should be used with runtime feature")
2032}
2033#[cfg(all(feature = "runtime", feature = "resource"))]
2034#[proc_macro_attribute]
2035#[proc_macro_error]
2036pub fn runtime_phy_cpu_core(attr: TokenStream, stream: TokenStream) -> TokenStream {
2037 let attr_str = attr.to_string().replace(' ', "");
2038 let core_limitation = match attr_str.parse::<usize>() {
2039 Ok(c) => c,
2040 Err(_) => abort_call_site!("physical core limitation is incorrect"),
2041 };
2042
2043 let ItemFn {
2044 attrs,
2045 vis,
2046 sig,
2047 block,
2048 } = parse_macro_input!(stream as ItemFn);
2049 let syn::Signature { ident, .. } = sig.clone();
2050 let check_ident = syn::Ident::new(
2051 &format!("_check_{}", ident.to_string()),
2052 proc_macro2::Span::call_site(),
2053 );
2054
2055 quote::quote! {
2056 fn #check_ident() -> Result<(), libtest_with::Failed> {
2057 if libtest_with::num_cpus::get_physical() >= #core_limitation {
2058 #ident();
2059 Ok(())
2060 } else {
2061 Err(format!("{}because the physical cpu core less than {}",
2062 libtest_with::RUNTIME_IGNORE_PREFIX, #core_limitation).into())
2063 }
2064 }
2065
2066 #(#attrs)*
2067 #vis #sig #block
2068
2069 }
2070 .into()
2071}
2072
2073#[proc_macro_attribute]
2107#[proc_macro_error]
2108#[cfg(feature = "executable")]
2109pub fn executable(attr: TokenStream, stream: TokenStream) -> TokenStream {
2110 if is_module(&stream) {
2111 mod_macro(
2112 attr,
2113 parse_macro_input!(stream as ItemMod),
2114 check_executable_condition,
2115 )
2116 } else {
2117 fn_macro(
2118 attr,
2119 parse_macro_input!(stream as ItemFn),
2120 check_executable_condition,
2121 )
2122 }
2123}
2124
2125#[cfg(feature = "executable")]
2126fn check_executable_condition(attr_str: String) -> (bool, String) {
2127 let executables: Vec<&str> = attr_str.split(',').collect();
2128 let mut missing_executables = vec![];
2129 for exe in executables.iter() {
2130 if which(exe.trim_matches('"')).is_err() {
2131 missing_executables.push(exe.to_string());
2132 }
2133 }
2134 let ignore_msg = if missing_executables.len() == 1 {
2135 format!("because executable not found: {}", missing_executables[0])
2136 } else {
2137 format!(
2138 "because following executables not found: \n{}\n",
2139 missing_executables.join("\n")
2140 )
2141 };
2142 (missing_executables.is_empty(), ignore_msg)
2143}
2144
2145#[cfg(not(feature = "runtime"))]
2158#[proc_macro_attribute]
2159#[proc_macro_error]
2160pub fn runtime_executable(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
2161 panic!("should be used with runtime feature")
2162}
2163#[cfg(all(feature = "runtime", feature = "executable"))]
2164#[proc_macro_attribute]
2165#[proc_macro_error]
2166pub fn runtime_executable(attr: TokenStream, stream: TokenStream) -> TokenStream {
2167 let attr_str = attr.to_string().replace(' ', "");
2168 let executables: Vec<&str> = attr_str.split(',').collect();
2169 let ItemFn {
2170 attrs,
2171 vis,
2172 sig,
2173 block,
2174 } = parse_macro_input!(stream as ItemFn);
2175 let syn::Signature { ident, .. } = sig.clone();
2176 let check_ident = syn::Ident::new(
2177 &format!("_check_{}", ident.to_string()),
2178 proc_macro2::Span::call_site(),
2179 );
2180
2181 quote::quote! {
2182 fn #check_ident() -> Result<(), libtest_with::Failed> {
2183 let mut missing_executables = vec![];
2184 #(
2185 if libtest_with::which::which(#executables).is_err() {
2186 missing_executables.push(#executables);
2187 }
2188 )*
2189 match missing_executables.len() {
2190 0 => {
2191 #ident();
2192 Ok(())
2193 },
2194 1 => Err(
2195 format!("{}because executable {} not found",
2196 libtest_with::RUNTIME_IGNORE_PREFIX, missing_executables[0]
2197 ).into()),
2198 _ => Err(
2199 format!("{}because following executables not found:\n{}\n",
2200 libtest_with::RUNTIME_IGNORE_PREFIX, missing_executables.join(", ")
2201 ).into()),
2202 }
2203 }
2204
2205 #(#attrs)*
2206 #vis #sig #block
2207
2208 }
2209 .into()
2210}
2211
2212#[cfg(not(feature = "runtime"))]
2234#[proc_macro]
2235pub fn runner(_input: TokenStream) -> TokenStream {
2236 panic!("should be used with runtime feature")
2237}
2238#[cfg(feature = "runtime")]
2239#[proc_macro]
2240pub fn runner(input: TokenStream) -> TokenStream {
2241 let input_str = input.to_string();
2242 let mod_names: Vec<syn::Ident> = input_str
2243 .split(",")
2244 .map(|s| syn::Ident::new(s.trim(), proc_macro2::Span::call_site()))
2245 .collect();
2246 quote::quote! {
2247 fn main() {
2248 let args = libtest_with::Arguments::from_args();
2249 let mut no_env_tests = Vec::new();
2250 #(
2251 match #mod_names::_runtime_tests() {
2252 (Some(env), tests) => {
2253 libtest_with::run(&args, tests).exit_if_failed();
2254 drop(env);
2255 },
2256 (None, mut tests) => no_env_tests.append(&mut tests),
2257 }
2258 )*
2259 libtest_with::run(&args, no_env_tests).exit();
2260 }
2261 }
2262 .into()
2263}
2264
2265#[cfg(not(feature = "runtime"))]
2380#[proc_macro_attribute]
2381#[proc_macro_error]
2382pub fn module(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
2383 panic!("should be used with runtime feature")
2384}
2385#[cfg(feature = "runtime")]
2386#[proc_macro_attribute]
2387#[proc_macro_error]
2388pub fn module(_attr: TokenStream, stream: TokenStream) -> TokenStream {
2389 let ItemMod {
2390 attrs,
2391 vis,
2392 mod_token,
2393 ident,
2394 content,
2395 ..
2396 } = parse_macro_input!(stream as ItemMod);
2397
2398 if let Some(content) = content {
2399 let content = content.1;
2400 if crate::utils::has_test_cfg(&attrs) {
2401 abort_call_site!("should not use `#[cfg(test)]` on the mod with `#[test_with::module]`")
2402 } else {
2403 let mut test_env_type = None;
2404 let test_names: Vec<String> = content
2405 .iter()
2406 .filter_map(|c| match c {
2407 Item::Fn(ItemFn {
2408 sig: syn::Signature { ident, .. },
2409 attrs,
2410 ..
2411 }) => match crate::utils::test_with_attrs(&attrs) {
2412 (true, true, _) => abort_call_site!(
2413 "should not use #[test] for method in `#[test_with::module]`"
2414 ),
2415 (_, true, false) => abort_call_site!(
2416 "use `#[test_with::runtime_*]` for method in `#[test_with::module]`"
2417 ),
2418 (false, true, true) => Some(ident.to_string()),
2419 _ => None,
2420 },
2421 Item::Struct(ItemStruct { ident, vis, .. })
2422 | Item::Type(ItemType { ident, vis, .. }) => {
2423 if ident.to_string() == "TestEnv" {
2424 match vis {
2425 syn::Visibility::Public(_) => test_env_type = Some(ident),
2426 _ => abort_call_site!("TestEnv should be pub for testing"),
2427 }
2428 }
2429 None
2430 }
2431 _ => None,
2432 })
2433 .collect();
2434 let check_names: Vec<syn::Ident> = test_names
2435 .iter()
2436 .map(|c| {
2437 syn::Ident::new(
2438 &format!("_check_{}", c.to_string()),
2439 proc_macro2::Span::call_site(),
2440 )
2441 })
2442 .collect();
2443 if let Some(test_env_type) = test_env_type {
2444 quote::quote! {
2445 #(#attrs)*
2446 #vis #mod_token #ident {
2447 use super::*;
2448 pub fn _runtime_tests() -> (Option<#test_env_type>, Vec<libtest_with::Trial>) {
2449 use libtest_with::Trial;
2450 (
2451 Some(#test_env_type::default()),
2452 vec![
2453 #(Trial::test(#test_names, #check_names),)*
2454 ]
2455 )
2456 }
2457 #(#content)*
2458 }
2459 }
2460 .into()
2461 } else {
2462 quote::quote! {
2463 #(#attrs)*
2464 #vis #mod_token #ident {
2465 use super::*;
2466 pub fn _runtime_tests() -> (Option<()>, Vec<libtest_with::Trial>) {
2467 use libtest_with::Trial;
2468 (
2469 None,
2470 vec![
2471 #(Trial::test(#test_names, #check_names),)*
2472 ]
2473 )
2474 }
2475 #(#content)*
2476 }
2477 }
2478 .into()
2479 }
2480 }
2481 } else {
2482 abort_call_site!("should use on mod with context")
2483 }
2484}
2485
2486#[cfg(not(feature = "runtime"))]
2504#[proc_macro_attribute]
2505#[proc_macro_error]
2506pub fn runtime_ignore_if(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
2507 panic!("should be used with runtime feature")
2508}
2509#[cfg(feature = "runtime")]
2510#[proc_macro_attribute]
2511#[proc_macro_error]
2512pub fn runtime_ignore_if(attr: TokenStream, stream: TokenStream) -> TokenStream {
2513 let ignore_function = syn::Ident::new(
2514 &attr.to_string().replace(' ', ""),
2515 proc_macro2::Span::call_site(),
2516 );
2517 let ItemFn {
2518 attrs,
2519 vis,
2520 sig,
2521 block,
2522 } = parse_macro_input!(stream as ItemFn);
2523 let syn::Signature { ident, .. } = sig.clone();
2524 let check_ident = syn::Ident::new(
2525 &format!("_check_{}", ident.to_string()),
2526 proc_macro2::Span::call_site(),
2527 );
2528 quote::quote! {
2529 fn #check_ident() -> Result<(), libtest_with::Failed> {
2530 if let Some(msg) = #ignore_function() {
2531 Err(format!("{}{msg}", libtest_with::RUNTIME_IGNORE_PREFIX).into())
2532 } else {
2533 #ident();
2534 Ok(())
2535 }
2536 }
2537
2538 #(#attrs)*
2539 #vis #sig #block
2540 }
2541 .into()
2542}
2543
2544#[cfg(test)]
2545mod tests {
2546 use super::{check_env_condition, check_no_env_condition};
2547
2548 mod env_macro {
2549 use super::*;
2550
2551 #[test]
2552 fn single_env_var_should_be_not_set() {
2553 let env_var = "A_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";
2555
2556 let attr_str = env_var.to_string();
2558
2559 let (is_ok, ignore_msg) = check_env_condition(attr_str);
2561
2562 assert!(!is_ok);
2565 assert!(ignore_msg.contains(env_var));
2567 }
2568
2569 #[test]
2570 fn multiple_env_vars_should_not_be_set() {
2571 let env_var1 = "A_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";
2573 let env_var2 = "ANOTHER_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";
2574
2575 let attr_str = format!("{}, {}", env_var1, env_var2);
2577
2578 let (is_ok, ignore_msg) = check_env_condition(attr_str);
2580
2581 assert!(!is_ok);
2584 assert!(ignore_msg.contains(env_var1));
2586 assert!(ignore_msg.contains(env_var2));
2587 }
2588
2589 #[test]
2590 fn single_env_var_should_be_set() {
2591 let env_var = "PATH";
2593
2594 let attr_str = env_var.to_string();
2596
2597 let (is_ok, ignore_msg) = check_env_condition(attr_str);
2599
2600 assert!(is_ok);
2603 assert!(!ignore_msg.contains(env_var));
2605 }
2606
2607 #[test]
2618 fn multiple_env_vars_should_be_set() {
2619 let env_var1 = "PATH";
2621 let env_var2 = "HOME";
2622
2623 let attr_str = format!("\t{},\n\t{}\n", env_var1, env_var2);
2625
2626 let (is_ok, ignore_msg) = check_env_condition(attr_str);
2628
2629 assert!(is_ok);
2632 assert!(!ignore_msg.contains(env_var1));
2634 assert!(!ignore_msg.contains(env_var2));
2635 }
2636
2637 #[test]
2640 fn multiple_env_vars_but_one_is_not_set() {
2641 let env_var1 = "PATH";
2643 let env_var2 = "HOME";
2644 let env_var3 = "A_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";
2645
2646 let attr_str = format!("{}, {}, {}", env_var1, env_var2, env_var3);
2648
2649 let (is_ok, ignore_msg) = check_env_condition(attr_str);
2651
2652 assert!(!is_ok);
2655 assert!(!ignore_msg.contains(env_var1));
2657 assert!(!ignore_msg.contains(env_var2));
2658 assert!(ignore_msg.contains(env_var3));
2659 }
2660
2661 #[test]
2664 fn multiple_env_vars_and_various_not_set() {
2665 let env_var1 = "PATH";
2667 let env_var2 = "A_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";
2668 let env_var3 = "ANOTHER_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";
2669
2670 let attr_str = format!("{}, {}, {}", env_var1, env_var2, env_var3);
2672
2673 let (is_ok, ignore_msg) = check_env_condition(attr_str);
2675
2676 assert!(!is_ok);
2679 assert!(!ignore_msg.contains(env_var1));
2681 assert!(ignore_msg.contains(env_var2));
2682 assert!(ignore_msg.contains(env_var3));
2683 }
2684 }
2685
2686 mod no_env_macro {
2687 use super::*;
2688
2689 #[test]
2690 fn single_env_var_not_set() {
2691 let env_var = "A_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";
2693
2694 let attr_str = env_var.to_string();
2696
2697 let (is_ok, ignore_msg) = check_no_env_condition(attr_str);
2699
2700 assert!(is_ok);
2703 assert!(!ignore_msg.contains(env_var));
2705 }
2706
2707 #[test]
2708 fn multiple_env_vars_not_set() {
2709 let env_var1 = "A_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";
2711 let env_var2 = "ANOTHER_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";
2712
2713 let attr_str = format!("{}, {}", env_var1, env_var2);
2715
2716 let (is_ok, ignore_msg) = check_no_env_condition(attr_str);
2718
2719 assert!(is_ok);
2722 assert!(!ignore_msg.contains(env_var1));
2724 assert!(!ignore_msg.contains(env_var2));
2725 }
2726
2727 #[test]
2728 fn single_env_var_set() {
2729 let env_var = "PATH";
2731
2732 let attr_str = env_var.to_string();
2734
2735 let (is_ok, ignore_msg) = check_no_env_condition(attr_str);
2737
2738 assert!(!is_ok);
2741 assert!(ignore_msg.contains(env_var));
2743 }
2744
2745 #[test]
2756 fn multiple_env_vars_set() {
2757 let env_var1 = "PATH";
2759 let env_var2 = "HOME";
2760
2761 let attr_str = format!("\t{},\n\t{}\n", env_var1, env_var2);
2763
2764 let (is_ok, ignore_msg) = check_no_env_condition(attr_str);
2766
2767 assert!(!is_ok);
2770 assert!(ignore_msg.contains(env_var1));
2772 assert!(ignore_msg.contains(env_var2));
2773 }
2774
2775 #[test]
2778 fn multiple_env_vars_but_one_is_set() {
2779 let env_var1 = "PATH";
2781 let env_var2 = "A_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";
2782 let env_var3 = "ANOTHER_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";
2783
2784 let attr_str = format!("{}, {}, {}", env_var1, env_var2, env_var3);
2786
2787 let (is_ok, ignore_msg) = check_no_env_condition(attr_str);
2789
2790 assert!(!is_ok);
2793 assert!(ignore_msg.contains(env_var1));
2795 assert!(!ignore_msg.contains(env_var2));
2796 assert!(!ignore_msg.contains(env_var3));
2797 }
2798
2799 #[test]
2802 fn multiple_env_vars_and_various_are_set() {
2803 let env_var1 = "PATH";
2805 let env_var2 = "HOME";
2806 let env_var3 = "A_RIDICULOUS_ENV_VAR_NAME_THAT_SHOULD_NOT_BE_SET";
2807
2808 let attr_str = format!("{}, {}, {}", env_var1, env_var2, env_var3);
2810
2811 let (is_ok, ignore_msg) = check_no_env_condition(attr_str);
2813
2814 assert!(!is_ok);
2817 assert!(ignore_msg.contains(env_var1));
2819 assert!(ignore_msg.contains(env_var2));
2820 assert!(!ignore_msg.contains(env_var3));
2821 }
2822 }
2823}
2824
2825#[proc_macro_attribute]
2863#[proc_macro_error]
2864pub fn lock(attr: TokenStream, stream: TokenStream) -> TokenStream {
2865 if is_module(&stream) {
2866 abort_call_site!("#[test_with::lock] only works with fn")
2867 } else {
2868 lock_macro(attr, parse_macro_input!(stream as ItemFn))
2869 }
2870}
2871
2872#[cfg(feature = "timezone")]
2907#[proc_macro_attribute]
2908#[proc_macro_error]
2909pub fn timezone(attr: TokenStream, stream: TokenStream) -> TokenStream {
2910 if is_module(&stream) {
2911 mod_macro(
2912 attr,
2913 parse_macro_input!(stream as ItemMod),
2914 check_tz_condition,
2915 )
2916 } else {
2917 fn_macro(
2918 attr,
2919 parse_macro_input!(stream as ItemFn),
2920 check_tz_condition,
2921 )
2922 }
2923}
2924
2925#[cfg(feature = "timezone")]
2926fn check_timezone(attr_str: &String) -> (bool, Vec<&str>) {
2927 let mut incorrect_tzs = vec![];
2928 let mut match_tz = false;
2929 let current_tz = chrono::Local::now().offset().local_minus_utc() / 60;
2930
2931 for tz in attr_str.split(',') {
2932 let parsed_tz = match tz {
2933 "NZDT" => Ok(13 * 60),
2934 "NZST" => Ok(12 * 60),
2935 "AEDT" => Ok(11 * 60),
2936 "ACDT" => Ok(10 * 60 + 30),
2937 "AEST" => Ok(10 * 60),
2938 "ACST" => Ok(9 * 60 + 30),
2939 "KST" | "JST" => Ok(9 * 60),
2940 "HKT" | "WITA" | "AWST" => Ok(8 * 60),
2941 "PST" => abort_call_site!("PST can be GMT+8 or GMT-8, please use +8 or -8 instead"),
2942 "WIB" => Ok(7 * 60),
2943 "CST" => abort_call_site!("PST can be GMT+8 or GMT-6, please use +8 or -6 instead"),
2944 "5.5" | "+5.5" => Ok(5 * 60 + 30),
2945 "IST" => abort_call_site!(
2946 "IST can be GMT+5.5, GMT+2 or GMT+1, please use +5.5, 2 or 1 instead"
2947 ),
2948 "PKT" => Ok(5 * 60),
2949 "EAT" | "EEST" | "IDT" | "MSK" => Ok(3 * 60),
2950 "CAT" | "EET" | "CEST" | "SAST" => Ok(2 * 60),
2951 "CET" | "WAT" | "WEST" | "BST" => Ok(1 * 60),
2952 "UTC" | "GMT" | "WET" => Ok(0),
2953 "NDT" | "-2.5" => Ok(-2 * 60 - 30),
2954 "NST" | "-3.5" => Ok(-3 * 60 - 30),
2955 "ADT" => Ok(-3 * 60),
2956 "AST" | "EDT" => Ok(-4 * 60),
2957 "EST" | "CDT" => Ok(-5 * 60),
2958 "MDT" => Ok(-6 * 60),
2959 "MST" | "PDT" => Ok(-7 * 60),
2960 "AKDT" => Ok(-8 * 60),
2961 "HDT" | "AKST" => Ok(-9 * 60),
2962 "HST" => Ok(-10 * 60),
2963 _ => tz.parse::<i32>().map(|tz| tz * 60),
2964 };
2965 if let Ok(parsed_tz) = parsed_tz {
2966 match_tz |= current_tz == parsed_tz;
2967 } else {
2968 incorrect_tzs.push(tz);
2969 }
2970 }
2971 (match_tz, incorrect_tzs)
2972}
2973
2974#[cfg(feature = "timezone")]
2975fn check_tz_condition(attr_str: String) -> (bool, String) {
2976 let (match_tz, incorrect_tzs) = check_timezone(&attr_str);
2977
2978 if incorrect_tzs.len() == 1 {
2980 (
2981 false,
2982 format!("because timezone {} is incorrect", incorrect_tzs[0]),
2983 )
2984 } else if incorrect_tzs.len() > 1 {
2985 (
2986 false,
2987 format!(
2988 "because following timezones are incorrect:\n{}\n",
2989 incorrect_tzs.join(", ")
2990 ),
2991 )
2992 } else if match_tz {
2993 (true, String::new())
2994 } else {
2995 (
2996 false,
2997 format!(
2998 "because the test case not run in following timezone:\n{}\n",
2999 attr_str
3000 ),
3001 )
3002 }
3003}
3004
3005#[cfg(not(feature = "runtime"))]
3018#[proc_macro_attribute]
3019#[proc_macro_error]
3020pub fn runtime_timezone(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
3021 panic!("should be used with runtime feature")
3022}
3023
3024#[cfg(all(feature = "runtime", feature = "timezone"))]
3025#[proc_macro_attribute]
3026#[proc_macro_error]
3027pub fn runtime_timezone(attr: TokenStream, stream: TokenStream) -> TokenStream {
3028 let attr_str = attr.to_string();
3029 let ItemFn {
3030 attrs,
3031 vis,
3032 sig,
3033 block,
3034 } = parse_macro_input!(stream as ItemFn);
3035 let syn::Signature { ident, .. } = sig.clone();
3036 let check_ident = syn::Ident::new(
3037 &format!("_check_{}", ident.to_string()),
3038 proc_macro2::Span::call_site(),
3039 );
3040 quote::quote! {
3041 fn #check_ident() -> Result<(), libtest_with::Failed> {
3042
3043 let mut incorrect_tzs = vec![];
3044 let mut match_tz = false;
3045 let current_tz = libtest_with::chrono::Local::now().offset().local_minus_utc() / 60;
3046 for tz in #attr_str.split(',') {
3047 if let Ok(parsed_tz) = tz.parse::<i32>() {
3048 match_tz |= current_tz == parsed_tz;
3049 } else {
3050 incorrect_tzs.push(tz);
3051 }
3052 }
3053
3054 if match_tz && incorrect_tzs.is_empty() {
3055 #ident();
3056 Ok(())
3057 } else if incorrect_tzs.len() == 1 {
3058 Err(
3059 format!("{}because timezone {} is incorrect",
3060 libtest_with::RUNTIME_IGNORE_PREFIX, incorrect_tzs[0]
3061 ).into())
3062 } else if incorrect_tzs.len() > 1 {
3063 Err(
3064 format!("{}because following timezones are incorrect:\n{:?}\n",
3065 libtest_with::RUNTIME_IGNORE_PREFIX, incorrect_tzs
3066 ).into())
3067 } else {
3068 Err(
3069 format!(
3070 "{}because the test case not run in following timezone:\n{}\n",
3071 libtest_with::RUNTIME_IGNORE_PREFIX,
3072 #attr_str
3073 ).into())
3074 }
3075 }
3076
3077 #(#attrs)*
3078 #vis #sig #block
3079 }
3080 .into()
3081}