test_with_derive/lib.rs
1use proc_macro::TokenStream;
2use syn::{parse_macro_input, ItemFn, ItemMod};
3
4#[cfg(feature = "runtime")]
5use syn::ReturnType;
6
7use crate::utils::{fn_macro, is_module, lock_macro, mod_macro};
8
9mod env;
10#[cfg(feature = "executable")]
11mod executable;
12mod file;
13#[cfg(feature = "http")]
14mod http;
15#[cfg(feature = "icmp")]
16mod icmp;
17#[cfg(feature = "resource")]
18mod resource;
19#[cfg(feature = "runtime")]
20mod runtime;
21mod socket;
22#[cfg(feature = "timezone")]
23mod timezone;
24#[cfg(feature = "user")]
25mod user;
26mod utils;
27
28/// Run test case when the environment variable is set.
29/// ```
30/// #[cfg(test)]
31/// mod tests {
32///
33/// // PWD environment variable exists
34/// #[test_with::env(PWD)]
35/// #[test]
36/// fn test_works() {
37/// assert!(true);
38/// }
39///
40/// // NOTHING environment variable does not exist
41/// #[test_with::env(NOTHING)]
42/// #[test]
43/// fn test_ignored() {
44/// panic!("should be ignored")
45/// }
46///
47/// // NOT_SAYING environment variable does not exist
48/// #[test_with::env(PWD, NOT_SAYING)]
49/// #[test]
50/// fn test_ignored_too() {
51/// panic!("should be ignored")
52/// }
53/// }
54/// ```
55/// or run all test cases for test module when the environment variable is set.
56/// ```
57/// #[test_with::env(PWD)]
58/// #[cfg(test)]
59/// mod tests {
60///
61/// #[test]
62/// fn test_works() {
63/// assert!(true);
64/// }
65/// }
66/// ```
67#[proc_macro_attribute]
68pub fn env(attr: TokenStream, stream: TokenStream) -> TokenStream {
69 if is_module(&stream) {
70 mod_macro(
71 attr,
72 parse_macro_input!(stream as ItemMod),
73 env::check_env_condition,
74 )
75 } else {
76 fn_macro(
77 attr,
78 parse_macro_input!(stream as ItemFn),
79 env::check_env_condition,
80 )
81 }
82}
83
84/// Run test case when the example running and the environment variable is set.
85///```rust
86/// // write as example in examples/*rs
87/// test_with::runner!(env);
88/// #[test_with::module]
89/// mod env {
90/// #[test_with::runtime_env(PWD)]
91/// fn test_works() {
92/// assert!(true);
93/// }
94/// }
95///```
96#[cfg(not(feature = "runtime"))]
97#[proc_macro_attribute]
98pub fn runtime_env(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
99 panic!("should be used with runtime feature")
100}
101#[cfg(feature = "runtime")]
102#[proc_macro_attribute]
103pub fn runtime_env(attr: TokenStream, stream: TokenStream) -> TokenStream {
104 env::runtime_env(attr, stream)
105}
106
107/// Ignore test case when the environment variable is set.
108/// ```
109/// #[cfg(test)]
110/// mod tests {
111///
112/// // The test will be ignored in GITHUB_ACTION
113/// #[test_with::no_env(GITHUB_ACTIONS)]
114/// #[test]
115/// fn test_ignore_in_github_action() {
116/// assert!(false);
117/// }
118/// }
119#[proc_macro_attribute]
120pub fn no_env(attr: TokenStream, stream: TokenStream) -> TokenStream {
121 if is_module(&stream) {
122 mod_macro(
123 attr,
124 parse_macro_input!(stream as ItemMod),
125 env::check_no_env_condition,
126 )
127 } else {
128 fn_macro(
129 attr,
130 parse_macro_input!(stream as ItemFn),
131 env::check_no_env_condition,
132 )
133 }
134}
135
136/// Ignore test case when the example running and the environment variable is set.
137///```rust
138/// // write as example in examples/*rs
139/// test_with::runner!(env);
140/// #[test_with::module]
141/// mod env {
142/// #[test_with::runtime_no_env(NOT_EXIST)]
143/// fn test_works() {
144/// assert!(true);
145/// }
146/// }
147///```
148#[cfg(not(feature = "runtime"))]
149#[proc_macro_attribute]
150pub fn runtime_no_env(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
151 panic!("should be used with runtime feature")
152}
153#[cfg(feature = "runtime")]
154#[proc_macro_attribute]
155pub fn runtime_no_env(attr: TokenStream, stream: TokenStream) -> TokenStream {
156 env::runtime_no_env(attr, stream)
157}
158
159/// Run test case when the file exist.
160/// ```
161/// #[cfg(test)]
162/// mod tests {
163///
164/// // hostname exists
165/// #[test_with::file(/etc/hostname)]
166/// #[test]
167/// fn test_works() {
168/// assert!(true);
169/// }
170///
171/// // nothing file does not exist
172/// #[test_with::file(/etc/nothing)]
173/// #[test]
174/// fn test_ignored() {
175/// panic!("should be ignored")
176/// }
177///
178/// // hostname and hosts exist
179/// #[test_with::file(/etc/hostname, /etc/hosts)]
180/// #[test]
181/// fn test_works_too() {
182/// assert!(true);
183/// }
184/// }
185/// ```
186#[proc_macro_attribute]
187pub fn file(attr: TokenStream, stream: TokenStream) -> TokenStream {
188 if is_module(&stream) {
189 mod_macro(
190 attr,
191 parse_macro_input!(stream as ItemMod),
192 file::check_file_condition,
193 )
194 } else {
195 fn_macro(
196 attr,
197 parse_macro_input!(stream as ItemFn),
198 file::check_file_condition,
199 )
200 }
201}
202
203/// Run test case when the example running and the file exist.
204///```rust
205/// // write as example in examples/*rs
206/// test_with::runner!(file);
207/// #[test_with::module]
208/// mod file {
209/// #[test_with::runtime_file(/etc/hostname)]
210/// fn test_works() {
211/// assert!(true);
212/// }
213/// }
214///```
215#[cfg(not(feature = "runtime"))]
216#[proc_macro_attribute]
217pub fn runtime_file(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
218 panic!("should be used with runtime feature")
219}
220#[cfg(feature = "runtime")]
221#[proc_macro_attribute]
222pub fn runtime_file(attr: TokenStream, stream: TokenStream) -> TokenStream {
223 file::runtime_file(attr, stream)
224}
225
226/// Run test case when the path(file or folder) exist.
227/// ```
228/// #[cfg(test)]
229/// mod tests {
230///
231/// // etc exists
232/// #[test_with::path(/etc)]
233/// #[test]
234/// fn test_works() {
235/// assert!(true);
236/// }
237///
238/// // nothing does not exist
239/// #[test_with::path(/nothing)]
240/// #[test]
241/// fn test_ignored() {
242/// panic!("should be ignored")
243/// }
244///
245/// // etc and tmp exist
246/// #[test_with::path(/etc, /tmp)]
247/// #[test]
248/// fn test_works_too() {
249/// assert!(true);
250/// }
251/// }
252/// ```
253#[proc_macro_attribute]
254pub fn path(attr: TokenStream, stream: TokenStream) -> TokenStream {
255 if is_module(&stream) {
256 mod_macro(
257 attr,
258 parse_macro_input!(stream as ItemMod),
259 file::check_path_condition,
260 )
261 } else {
262 fn_macro(
263 attr,
264 parse_macro_input!(stream as ItemFn),
265 file::check_path_condition,
266 )
267 }
268}
269
270/// Run test case when the example running and the path(file or folder) exist.
271///```rust
272/// // write as example in examples/*rs
273/// test_with::runner!(path);
274/// #[test_with::module]
275/// mod path {
276/// #[test_with::runtime_path(/etc)]
277/// fn test_works() {
278/// assert!(true);
279/// }
280/// }
281///```
282#[cfg(not(feature = "runtime"))]
283#[proc_macro_attribute]
284pub fn runtime_path(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
285 panic!("should be used with runtime feature")
286}
287#[cfg(feature = "runtime")]
288#[proc_macro_attribute]
289pub fn runtime_path(attr: TokenStream, stream: TokenStream) -> TokenStream {
290 file::runtime_path(attr, stream)
291}
292
293/// Run test case when the http service exist.
294/// ```
295/// #[cfg(test)]
296/// mod tests {
297///
298/// // http service exists
299/// #[test_with::http(httpbin.org)]
300/// #[test]
301/// fn test_works() {
302/// assert!(true);
303/// }
304///
305/// // There is no not.exist.com
306/// #[test_with::http(not.exist.com)]
307/// #[test]
308/// fn test_ignored() {
309/// panic!("should be ignored")
310/// }
311/// }
312/// ```
313#[proc_macro_attribute]
314#[cfg(feature = "http")]
315pub fn http(attr: TokenStream, stream: TokenStream) -> TokenStream {
316 if is_module(&stream) {
317 mod_macro(
318 attr,
319 parse_macro_input!(stream as ItemMod),
320 http::check_http_condition,
321 )
322 } else {
323 fn_macro(
324 attr,
325 parse_macro_input!(stream as ItemFn),
326 http::check_http_condition,
327 )
328 }
329}
330
331/// Run test case when the example running and the http service exist.
332///```rust
333/// // write as example in examples/*rs
334/// test_with::runner!(http);
335/// #[test_with::module]
336/// mod http {
337/// #[test_with::runtime_http(httpbin.org)]
338/// fn test_works() {
339/// assert!(true);
340/// }
341/// }
342#[cfg(not(feature = "runtime"))]
343#[proc_macro_attribute]
344pub fn runtime_http(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
345 panic!("should be used with runtime feature")
346}
347
348#[cfg(all(feature = "runtime", feature = "http"))]
349#[proc_macro_attribute]
350pub fn runtime_http(attr: TokenStream, stream: TokenStream) -> TokenStream {
351 http::runtime_http(attr, stream)
352}
353
354/// Run test case when the https service exist.
355/// ```
356/// #[cfg(test)]
357/// mod tests {
358///
359/// // https server exists
360/// #[test_with::https(www.rust-lang.org)]
361/// #[test]
362/// fn test_works() {
363/// assert!(true);
364/// }
365///
366/// // There is no not.exist.com
367/// #[test_with::https(not.exist.com)]
368/// #[test]
369/// fn test_ignored() {
370/// panic!("should be ignored")
371/// }
372/// }
373/// ```
374#[proc_macro_attribute]
375#[cfg(feature = "http")]
376pub fn https(attr: TokenStream, stream: TokenStream) -> TokenStream {
377 if is_module(&stream) {
378 mod_macro(
379 attr,
380 parse_macro_input!(stream as ItemMod),
381 http::check_https_condition,
382 )
383 } else {
384 fn_macro(
385 attr,
386 parse_macro_input!(stream as ItemFn),
387 http::check_https_condition,
388 )
389 }
390}
391
392/// Run test case when the example running and the http service exist.
393///```rust
394/// // write as example in examples/*rs
395/// test_with::runner!(http);
396/// #[test_with::module]
397/// mod http {
398/// #[test_with::runtime_https(httpbin.org)]
399/// fn test_works() {
400/// assert!(true);
401/// }
402/// }
403#[cfg(all(not(feature = "runtime"), feature = "http"))]
404#[proc_macro_attribute]
405pub fn runtime_https(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
406 panic!("should be used with runtime feature")
407}
408
409#[cfg(all(feature = "runtime", feature = "http"))]
410#[proc_macro_attribute]
411pub fn runtime_https(attr: TokenStream, stream: TokenStream) -> TokenStream {
412 http::runtime_https(attr, stream)
413}
414
415/// Run test case when the server online.
416/// Please make sure the role of test case runner have capability to open socket
417///
418/// ```
419/// #[cfg(test)]
420/// mod tests {
421///
422/// // localhost is online
423/// #[test_with::icmp(127.0.0.1)]
424/// #[test]
425/// fn test_works() {
426/// assert!(true);
427/// }
428///
429/// // 193.194.195.196 is offline
430/// #[test_with::icmp(193.194.195.196)]
431/// #[test]
432/// fn test_ignored() {
433/// panic!("should be ignored")
434/// }
435/// }
436/// ```
437#[proc_macro_attribute]
438#[cfg(feature = "icmp")]
439pub fn icmp(attr: TokenStream, stream: TokenStream) -> TokenStream {
440 if is_module(&stream) {
441 mod_macro(
442 attr,
443 parse_macro_input!(stream as ItemMod),
444 icmp::check_icmp_condition,
445 )
446 } else {
447 fn_macro(
448 attr,
449 parse_macro_input!(stream as ItemFn),
450 icmp::check_icmp_condition,
451 )
452 }
453}
454
455/// Run test case when the example running and the server online.
456/// Please make sure the role of test case runner have capability to open socket
457///```rust
458/// // write as example in examples/*rs
459/// test_with::runner!(icmp);
460/// #[test_with::module]
461/// mod icmp {
462/// // 193.194.195.196 is offline
463/// #[test_with::runtime_icmp(193.194.195.196)]
464/// fn test_ignored_with_non_existing_host() {
465/// panic!("should be ignored with non existing host")
466/// }
467/// }
468#[cfg(all(not(feature = "runtime"), feature = "icmp"))]
469#[proc_macro_attribute]
470pub fn runtime_icmp(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
471 panic!("should be used with runtime feature")
472}
473
474#[cfg(all(feature = "runtime", feature = "icmp"))]
475#[proc_macro_attribute]
476pub fn runtime_icmp(attr: TokenStream, stream: TokenStream) -> TokenStream {
477 icmp::runtime_icmp(attr, stream)
478}
479
480/// Run test case when socket connected
481///
482/// ```
483/// #[cfg(test)]
484/// mod tests {
485///
486/// // Google DNS is online
487/// #[test_with::tcp(8.8.8.8:53)]
488/// #[test]
489/// fn test_works() {
490/// assert!(true);
491/// }
492///
493/// // 193.194.195.196 is offline
494/// #[test_with::tcp(193.194.195.196)]
495/// #[test]
496/// fn test_ignored() {
497/// panic!("should be ignored")
498/// }
499/// }
500/// ```
501#[proc_macro_attribute]
502pub fn tcp(attr: TokenStream, stream: TokenStream) -> TokenStream {
503 if is_module(&stream) {
504 mod_macro(
505 attr,
506 parse_macro_input!(stream as ItemMod),
507 socket::check_tcp_condition,
508 )
509 } else {
510 fn_macro(
511 attr,
512 parse_macro_input!(stream as ItemFn),
513 socket::check_tcp_condition,
514 )
515 }
516}
517
518/// Run test case when the example running and socket connected
519///```rust
520/// // write as example in examples/*rs
521/// test_with::runner!(tcp);
522/// #[test_with::module]
523/// mod tcp {
524/// // Google DNS is online
525/// #[test_with::runtime_tcp(8.8.8.8:53)]
526/// fn test_works_with_DNS_server() {
527/// assert!(true);
528/// }
529/// }
530#[cfg(not(feature = "runtime"))]
531#[proc_macro_attribute]
532pub fn runtime_tcp(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
533 panic!("should be used with runtime feature")
534}
535
536#[cfg(feature = "runtime")]
537#[proc_macro_attribute]
538pub fn runtime_tcp(attr: TokenStream, stream: TokenStream) -> TokenStream {
539 socket::runtime_tcp(attr, stream)
540}
541
542/// Run test case when runner is root
543///
544/// ```
545/// #[cfg(test)]
546/// mod tests {
547///
548/// // Only works with root account
549/// #[test_with::root()]
550/// #[test]
551/// fn test_ignored() {
552/// panic!("should be ignored")
553/// }
554/// }
555/// ```
556#[proc_macro_attribute]
557#[cfg(all(feature = "user"))]
558pub fn root(attr: TokenStream, stream: TokenStream) -> TokenStream {
559 if is_module(&stream) {
560 mod_macro(
561 attr,
562 parse_macro_input!(stream as ItemMod),
563 user::check_root_condition,
564 )
565 } else {
566 fn_macro(
567 attr,
568 parse_macro_input!(stream as ItemFn),
569 user::check_root_condition,
570 )
571 }
572}
573
574/// Run test case when runner is root
575///```rust
576/// // write as example in examples/*rs
577/// test_with::runner!(user);
578/// #[test_with::module]
579/// mod user {
580/// // Google DNS is online
581/// #[test_with::runtime_root()]
582/// fn test_ignored() {
583/// panic!("should be ignored")
584/// }
585/// }
586#[cfg(not(feature = "runtime"))]
587#[proc_macro_attribute]
588pub fn runtime_root(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
589 panic!("should be used with runtime feature")
590}
591#[cfg(all(feature = "runtime", feature = "user"))]
592#[proc_macro_attribute]
593pub fn runtime_root(attr: TokenStream, stream: TokenStream) -> TokenStream {
594 user::runtime_root(attr, stream)
595}
596
597/// Run test case when runner in group
598///
599/// ```
600/// #[cfg(test)]
601/// mod tests {
602///
603/// // Only works with group avengers
604/// #[test_with::group(avengers)]
605/// #[test]
606/// fn test_ignored() {
607/// panic!("should be ignored")
608/// }
609/// }
610/// ```
611#[proc_macro_attribute]
612#[cfg(all(feature = "user"))]
613pub fn group(attr: TokenStream, stream: TokenStream) -> TokenStream {
614 if is_module(&stream) {
615 mod_macro(
616 attr,
617 parse_macro_input!(stream as ItemMod),
618 user::check_group_condition,
619 )
620 } else {
621 fn_macro(
622 attr,
623 parse_macro_input!(stream as ItemFn),
624 user::check_group_condition,
625 )
626 }
627}
628
629/// Run test case when runner in group
630///```rust
631/// // write as example in examples/*rs
632/// test_with::runner!(user);
633/// #[test_with::module]
634/// mod user {
635/// // Only works with group avengers
636/// #[test_with::runtime_group(avengers)]
637/// fn test_ignored() {
638/// panic!("should be ignored")
639/// }
640/// }
641#[cfg(not(feature = "runtime"))]
642#[proc_macro_attribute]
643pub fn runtime_group(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
644 panic!("should be used with runtime feature")
645}
646#[cfg(all(feature = "runtime", feature = "user"))]
647#[proc_macro_attribute]
648pub fn runtime_group(attr: TokenStream, stream: TokenStream) -> TokenStream {
649 user::runtime_group(attr, stream)
650}
651
652/// Run test case when runner is specific user
653///
654/// ```
655/// #[cfg(test)]
656/// mod tests {
657///
658/// // Only works with user
659/// #[test_with::user(spider)]
660/// #[test]
661/// fn test_ignored() {
662/// panic!("should be ignored")
663/// }
664/// }
665/// ```
666#[proc_macro_attribute]
667#[cfg(all(feature = "user", not(target_os = "windows")))]
668pub fn user(attr: TokenStream, stream: TokenStream) -> TokenStream {
669 if is_module(&stream) {
670 mod_macro(
671 attr,
672 parse_macro_input!(stream as ItemMod),
673 user::check_user_condition,
674 )
675 } else {
676 fn_macro(
677 attr,
678 parse_macro_input!(stream as ItemFn),
679 user::check_user_condition,
680 )
681 }
682}
683
684/// Run test case when runner is specific user
685///```rust
686/// // write as example in examples/*rs
687/// test_with::runner!(user);
688/// #[test_with::module]
689/// mod user {
690/// // Only works with user
691/// #[test_with::runtime_user(spider)]
692/// fn test_ignored() {
693/// panic!("should be ignored")
694/// }
695/// }
696#[cfg(not(feature = "runtime"))]
697#[proc_macro_attribute]
698pub fn runtime_user(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
699 panic!("should be used with runtime feature")
700}
701#[cfg(all(feature = "runtime", feature = "user"))]
702#[proc_macro_attribute]
703pub fn runtime_user(attr: TokenStream, stream: TokenStream) -> TokenStream {
704 user::runtime_user(attr, stream)
705}
706
707/// Run test case when memory size enough
708///
709/// ```
710/// #[cfg(test)]
711/// mod tests {
712///
713/// // Only works with enough memory size
714/// #[test_with::mem(100GB)]
715/// #[test]
716/// fn test_ignored() {
717/// panic!("should be ignored")
718/// }
719/// }
720/// ```
721#[proc_macro_attribute]
722#[cfg(feature = "resource")]
723pub fn mem(attr: TokenStream, stream: TokenStream) -> TokenStream {
724 if is_module(&stream) {
725 mod_macro(
726 attr,
727 parse_macro_input!(stream as ItemMod),
728 resource::check_mem_condition,
729 )
730 } else {
731 fn_macro(
732 attr,
733 parse_macro_input!(stream as ItemFn),
734 resource::check_mem_condition,
735 )
736 }
737}
738
739/// Run test case when the example running and memory size enough
740///```rust
741/// // write as example in examples/*rs
742/// test_with::runner!(resource);
743/// #[test_with::module]
744/// mod resource {
745/// // Only works with enough memory size
746/// #[test_with::runtime_mem(100GB)]
747/// fn test_ignored_mem_not_enough() {
748/// panic!("should be ignored")
749/// }
750/// }
751#[cfg(not(feature = "runtime"))]
752#[proc_macro_attribute]
753pub fn runtime_mem(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
754 panic!("should be used with runtime feature")
755}
756#[cfg(all(feature = "runtime", feature = "resource"))]
757#[proc_macro_attribute]
758pub fn runtime_mem(attr: TokenStream, stream: TokenStream) -> TokenStream {
759 resource::runtime_mem(attr, stream)
760}
761
762/// Run test case when the example running and free memory size enough
763///```rust
764/// // write as example in examples/*rs
765/// test_with::runner!(resource);
766/// #[test_with::module]
767/// mod resource {
768/// // Only works with enough free memory size
769/// #[test_with::runtime_free_mem(100GB)]
770/// fn test_ignored_free_mem_not_enough() {
771/// panic!("should be ignored")
772/// }
773/// }
774#[cfg(not(feature = "runtime"))]
775#[proc_macro_attribute]
776pub fn runtime_free_mem(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
777 panic!("should be used with runtime feature")
778}
779#[cfg(all(feature = "runtime", feature = "resource"))]
780#[proc_macro_attribute]
781pub fn runtime_free_mem(attr: TokenStream, stream: TokenStream) -> TokenStream {
782 resource::runtime_free_mem(attr, stream)
783}
784
785/// Run test case when the example running and available memory size enough
786///```rust
787/// // write as example in examples/*rs
788/// test_with::runner!(resource);
789/// #[test_with::module]
790/// mod resource {
791/// // Only works with enough available memory size
792/// #[test_with::runtime_available_mem(100GB)]
793/// fn test_ignored_available_mem_not_enough() {
794/// panic!("should be ignored")
795/// }
796/// }
797#[cfg(not(feature = "runtime"))]
798#[proc_macro_attribute]
799pub fn runtime_available_mem(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
800 panic!("should be used with runtime feature")
801}
802#[cfg(all(feature = "runtime", feature = "resource"))]
803#[proc_macro_attribute]
804pub fn runtime_available_mem(attr: TokenStream, stream: TokenStream) -> TokenStream {
805 resource::runtime_available_mem(attr, stream)
806}
807
808/// Run test case when swap size enough
809///
810/// ```
811/// #[cfg(test)]
812/// mod tests {
813///
814/// // Only works with enough swap size
815/// #[test_with::swap(100GB)]
816/// #[test]
817/// fn test_ignored() {
818/// panic!("should be ignored")
819/// }
820/// }
821/// ```
822#[proc_macro_attribute]
823#[cfg(feature = "resource")]
824pub fn swap(attr: TokenStream, stream: TokenStream) -> TokenStream {
825 if is_module(&stream) {
826 mod_macro(
827 attr,
828 parse_macro_input!(stream as ItemMod),
829 resource::check_swap_condition,
830 )
831 } else {
832 fn_macro(
833 attr,
834 parse_macro_input!(stream as ItemFn),
835 resource::check_swap_condition,
836 )
837 }
838}
839
840/// Run test case when the example running and swap enough
841///```rust
842/// // write as example in examples/*rs
843/// test_with::runner!(resource);
844/// #[test_with::module]
845/// mod resource {
846/// // Only works with enough swap size
847/// #[test_with::runtime_swap(100GB)]
848/// fn test_ignored_swap_not_enough() {
849/// panic!("should be ignored")
850/// }
851/// }
852#[cfg(not(feature = "runtime"))]
853#[proc_macro_attribute]
854pub fn runtime_swap(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
855 panic!("should be used with runtime feature")
856}
857#[cfg(all(feature = "runtime", feature = "resource"))]
858#[proc_macro_attribute]
859pub fn runtime_swap(attr: TokenStream, stream: TokenStream) -> TokenStream {
860 let swap_limitation_str = attr.to_string().replace(' ', "");
861 if byte_unit::Byte::parse_str(&swap_limitation_str, true).is_err() {
862 return syn::Error::new(
863 proc_macro2::Span::call_site(),
864 "swap size description is not correct",
865 )
866 .to_compile_error()
867 .into();
868 }
869
870 let ItemFn {
871 attrs,
872 vis,
873 sig,
874 block,
875 } = parse_macro_input!(stream as ItemFn);
876 let syn::Signature { ident, .. } = sig.clone();
877 let check_ident = syn::Ident::new(&format!("_check_{ident}"), proc_macro2::Span::call_site());
878
879 let check_fn = match (&sig.asyncness, &sig.output) {
880 (Some(_), ReturnType::Default) => quote::quote! {
881 async fn #check_ident() -> Result<test_with::Completion, test_with::Failed> {
882 let sys = test_with::sysinfo::System::new_with_specifics(
883 test_with::sysinfo::RefreshKind::nothing().with_memory(test_with::sysinfo::MemoryRefreshKind::nothing().with_swap()),
884 );
885 let swap_size = match test_with::byte_unit::Byte::parse_str(format!("{} B", sys.total_swap()), false) {
886 Ok(b) => b,
887 Err(_) => panic!("system swap size can not get"),
888 };
889 let swap_size_limitation = test_with::byte_unit::Byte::parse_str(#swap_limitation_str, true).expect("swap limitation should correct");
890 if swap_size >= swap_size_limitation {
891 #ident().await;
892 Ok(test_with::Completion::Completed)
893 } else {
894 Ok(test_with::Completion::ignored_with(format!("because the swap less than {}", #swap_limitation_str)))
895 }
896 }
897 },
898 (Some(_), ReturnType::Type(_, _)) => quote::quote! {
899 async fn #check_ident() -> Result<test_with::Completion, test_with::Failed> {
900 let sys = test_with::sysinfo::System::new_with_specifics(
901 test_with::sysinfo::RefreshKind::nothing().with_memory(test_with::sysinfo::MemoryRefreshKind::nothing().with_swap()),
902 );
903 let swap_size = match test_with::byte_unit::Byte::parse_str(format!("{} B", sys.total_swap()), false) {
904 Ok(b) => b,
905 Err(_) => panic!("system swap size can not get"),
906 };
907 let swap_size_limitation = test_with::byte_unit::Byte::parse_str(#swap_limitation_str, true).expect("swap limitation should correct");
908 if swap_size >= swap_size_limitation {
909 if let Err(e) = #ident().await {
910 Err(format!("{e:?}").into())
911 } else {
912 Ok(test_with::Completion::Completed)
913 }
914 } else {
915 Ok(test_with::Completion::ignored_with(format!("because the swap less than {}", #swap_limitation_str)))
916 }
917 }
918 },
919 (None, _) => quote::quote! {
920 fn #check_ident() -> Result<test_with::Completion, test_with::Failed> {
921 let sys = test_with::sysinfo::System::new_with_specifics(
922 test_with::sysinfo::RefreshKind::nothing().with_memory(test_with::sysinfo::MemoryRefreshKind::nothing().with_swap()),
923 );
924 let swap_size = match test_with::byte_unit::Byte::parse_str(format!("{} B", sys.total_swap()), false) {
925 Ok(b) => b,
926 Err(_) => panic!("system swap size can not get"),
927 };
928 let swap_size_limitation = test_with::byte_unit::Byte::parse_str(#swap_limitation_str, true).expect("swap limitation should correct");
929 if swap_size >= swap_size_limitation {
930 #ident();
931 Ok(test_with::Completion::Completed)
932 } else {
933 Ok(test_with::Completion::ignored_with(format!("because the swap less than {}", #swap_limitation_str)))
934 }
935 }
936 },
937 };
938
939 quote::quote! {
940 #check_fn
941 #(#attrs)*
942 #vis #sig #block
943 }
944 .into()
945}
946
947/// Run test case when the example running and free swap enough
948///```rust
949/// // write as example in examples/*rs
950/// test_with::runner!(resource);
951/// #[test_with::module]
952/// mod resource {
953/// // Only works with enough free swap size
954/// #[test_with::runtime_free_swap(100GB)]
955/// fn test_ignored_free_swap_not_enough() {
956/// panic!("should be ignored")
957/// }
958/// }
959#[cfg(not(feature = "runtime"))]
960#[proc_macro_attribute]
961pub fn runtime_free_swap(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
962 panic!("should be used with runtime feature")
963}
964#[cfg(all(feature = "runtime", feature = "resource"))]
965#[proc_macro_attribute]
966pub fn runtime_free_swap(attr: TokenStream, stream: TokenStream) -> TokenStream {
967 resource::runtime_free_swap(attr, stream)
968}
969
970/// Run test case when cpu core enough
971///
972/// ```
973/// #[cfg(test)]
974/// mod tests {
975///
976/// // Only works with enough cpu core
977/// #[test_with::cpu_core(64)]
978/// #[test]
979/// fn test_ignored() {
980/// panic!("should be ignored")
981/// }
982/// }
983/// ```
984#[proc_macro_attribute]
985#[cfg(feature = "resource")]
986pub fn cpu_core(attr: TokenStream, stream: TokenStream) -> TokenStream {
987 if is_module(&stream) {
988 mod_macro(
989 attr,
990 parse_macro_input!(stream as ItemMod),
991 resource::check_cpu_core_condition,
992 )
993 } else {
994 fn_macro(
995 attr,
996 parse_macro_input!(stream as ItemFn),
997 resource::check_cpu_core_condition,
998 )
999 }
1000}
1001
1002/// Run test case when cpu core enough
1003///```rust
1004/// // write as example in examples/*rs
1005/// test_with::runner!(resource);
1006/// #[test_with::module]
1007/// mod resource {
1008/// // Only works with enough cpu core
1009/// #[test_with::runtime_cpu_core(32)]
1010/// fn test_ignored_core_not_enough() {
1011/// panic!("should be ignored")
1012/// }
1013/// }
1014#[cfg(not(feature = "runtime"))]
1015#[proc_macro_attribute]
1016pub fn runtime_cpu_core(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1017 panic!("should be used with runtime feature")
1018}
1019#[cfg(all(feature = "runtime", feature = "resource"))]
1020#[proc_macro_attribute]
1021pub fn runtime_cpu_core(attr: TokenStream, stream: TokenStream) -> TokenStream {
1022 resource::runtime_cpu_core(attr, stream)
1023}
1024
1025/// Run test case when physical cpu core enough
1026///
1027/// ```
1028/// #[cfg(test)]
1029/// mod tests {
1030///
1031/// // Only works with enough cpu core
1032/// #[test_with::phy_core(64)]
1033/// #[test]
1034/// fn test_ignored() {
1035/// panic!("should be ignored")
1036/// }
1037/// }
1038/// ```
1039#[proc_macro_attribute]
1040#[cfg(feature = "resource")]
1041pub fn phy_core(attr: TokenStream, stream: TokenStream) -> TokenStream {
1042 if is_module(&stream) {
1043 mod_macro(
1044 attr,
1045 parse_macro_input!(stream as ItemMod),
1046 resource::check_cpu_core_condition,
1047 )
1048 } else {
1049 fn_macro(
1050 attr,
1051 parse_macro_input!(stream as ItemFn),
1052 resource::check_phy_core_condition,
1053 )
1054 }
1055}
1056
1057/// Run test case when physical core enough
1058///```rust
1059/// // write as example in examples/*rs
1060/// test_with::runner!(resource);
1061/// #[test_with::module]
1062/// mod resource {
1063/// // Only works with enough physical cpu core
1064/// #[test_with::runtime_phy_cpu_core(32)]
1065/// fn test_ignored_phy_core_not_enough() {
1066/// panic!("should be ignored")
1067/// }
1068/// }
1069#[cfg(not(feature = "runtime"))]
1070#[proc_macro_attribute]
1071pub fn runtime_phy_cpu_core(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1072 panic!("should be used with runtime feature")
1073}
1074#[cfg(all(feature = "runtime", feature = "resource"))]
1075#[proc_macro_attribute]
1076pub fn runtime_phy_cpu_core(attr: TokenStream, stream: TokenStream) -> TokenStream {
1077 resource::runtime_phy_cpu_core(attr, stream)
1078}
1079
1080/// Run test case when the executables exist.
1081/// ```
1082/// #[cfg(test)]
1083/// mod tests {
1084/// // `pwd` executable command exists
1085/// #[test_with::executable(pwd)]
1086/// #[test]
1087/// fn test_executable() {
1088/// assert!(true);
1089/// }
1090///
1091/// // `/bin/sh` executable exists
1092/// #[test_with::executable(/bin/sh)]
1093/// #[test]
1094/// fn test_executable_with_path() {
1095/// assert!(true);
1096/// }
1097///
1098/// // `non` does not exist
1099/// #[test_with::executable(non)]
1100/// #[test]
1101/// fn test_non_existing_executable() {
1102/// panic!("should be ignored")
1103/// }
1104///
1105/// // `pwd` and `ls` exist
1106/// #[test_with::executable(pwd, ls)]
1107/// #[test]
1108/// fn test_executables_too() {
1109/// assert!(true);
1110/// }
1111///
1112/// // `non` or `ls` exist
1113/// #[test_with::executable(non || ls)]
1114/// #[test]
1115/// fn test_one_of_executables_exist() {
1116/// assert!(true);
1117/// }
1118/// }
1119/// ```
1120#[proc_macro_attribute]
1121#[cfg(feature = "executable")]
1122pub fn executable(attr: TokenStream, stream: TokenStream) -> TokenStream {
1123 if is_module(&stream) {
1124 mod_macro(
1125 attr,
1126 parse_macro_input!(stream as ItemMod),
1127 executable::check_executable_condition,
1128 )
1129 } else {
1130 fn_macro(
1131 attr,
1132 parse_macro_input!(stream as ItemFn),
1133 executable::check_executable_condition,
1134 )
1135 }
1136}
1137
1138/// Run test case when the executable existing
1139///```rust
1140/// // write as example in examples/*rs
1141/// test_with::runner!(exe);
1142/// #[test_with::module]
1143/// mod exe {
1144/// // `/bin/sh` executable exists
1145/// #[test_with::runtime_executable(/bin/sh)]
1146/// fn test_executable_with_path() {
1147/// assert!(true);
1148/// }
1149/// }
1150#[cfg(not(feature = "runtime"))]
1151#[proc_macro_attribute]
1152pub fn runtime_executable(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1153 panic!("should be used with runtime feature")
1154}
1155#[cfg(all(feature = "runtime", feature = "executable"))]
1156#[proc_macro_attribute]
1157pub fn runtime_executable(attr: TokenStream, stream: TokenStream) -> TokenStream {
1158 executable::runtime_executable(attr, stream)
1159}
1160
1161/// Ignore test case when function return some reason
1162/// The function should be `fn() -> Option<String>`
1163/// ```
1164/// test_with::runner!(custom_mod);
1165///
1166/// fn something_happened() -> Option<String> {
1167/// Some("because something happened".to_string())
1168/// }
1169///
1170/// #[test_with::module]
1171/// mod custom_mod {
1172/// #[test_with::runtime_ignore_if(something_happened)]
1173/// fn test_ignored() {
1174/// assert!(false);
1175/// }
1176/// }
1177/// ```
1178#[cfg(not(feature = "runtime"))]
1179#[proc_macro_attribute]
1180pub fn runtime_ignore_if(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1181 panic!("should be used with runtime feature")
1182}
1183#[cfg(feature = "runtime")]
1184#[proc_macro_attribute]
1185pub fn runtime_ignore_if(attr: TokenStream, stream: TokenStream) -> TokenStream {
1186 let ignore_function = syn::Ident::new(
1187 &attr.to_string().replace(' ', ""),
1188 proc_macro2::Span::call_site(),
1189 );
1190 let ItemFn {
1191 attrs,
1192 vis,
1193 sig,
1194 block,
1195 } = parse_macro_input!(stream as ItemFn);
1196 let syn::Signature { ident, .. } = sig.clone();
1197 let check_ident = syn::Ident::new(&format!("_check_{ident}"), proc_macro2::Span::call_site());
1198
1199 let check_fn = match (&sig.asyncness, &sig.output) {
1200 (Some(_), ReturnType::Default) => quote::quote! {
1201 async fn #check_ident() -> Result<test_with::Completion, test_with::Failed> {
1202 if let Some(msg) = #ignore_function() {
1203 Ok(test_with::Completion::ignored_with(msg))
1204 } else {
1205 #ident().await;
1206 Ok(test_with::Completion::Completed)
1207 }
1208 }
1209 },
1210 (Some(_), ReturnType::Type(_, _)) => quote::quote! {
1211 async fn #check_ident() -> Result<test_with::Completion, test_with::Failed> {
1212 if let Some(msg) = #ignore_function() {
1213 Ok(test_with::Completion::ignored_with(msg))
1214 } else {
1215 if let Err(e) = #ident().await {
1216 Err(format!("{e:?}").into())
1217 } else {
1218 Ok(test_with::Completion::Completed)
1219 }
1220 }
1221 }
1222 },
1223 (None, _) => quote::quote! {
1224 fn #check_ident() -> Result<test_with::Completion, test_with::Failed> {
1225 if let Some(msg) = #ignore_function() {
1226 Ok(test_with::Completion::ignored_with(msg))
1227 } else {
1228 #ident();
1229 Ok(test_with::Completion::Completed)
1230 }
1231 }
1232 },
1233 };
1234
1235 quote::quote! {
1236 #check_fn
1237 #(#attrs)*
1238 #vis #sig #block
1239 }
1240 .into()
1241}
1242
1243/// Run test case one by one when the lock is acquired
1244/// It will automatically implement a file lock for the test case to prevent it run in the same
1245/// time. Also, you can pass the second parameter to specific the waiting seconds, default will be
1246/// 60 seconds.
1247/// ```
1248/// #[cfg(test)]
1249/// mod tests {
1250///
1251/// // `LOCK` is file based lock to prevent test1 an test2 run at the same time
1252/// #[test_with::lock(LOCK)]
1253/// #[test]
1254/// fn test_1() {
1255/// assert!(true);
1256/// }
1257///
1258/// // `LOCK` is file based lock to prevent test1 an test2 run at the same time
1259/// #[test_with::lock(LOCK)]
1260/// #[test]
1261/// fn test_2() {
1262/// assert!(true);
1263/// }
1264///
1265/// // `ANOTHER_LOCK` is file based lock to prevent test3 an test4 run at the same time with 3 sec
1266/// // waiting time.
1267/// #[test_with::lock(ANOTHER_LOCK, 3)]
1268/// fn test_3() {
1269/// assert!(true);
1270/// }
1271///
1272/// // `ANOTHER_LOCK` is file based lock to prevent test3 an test4 run at the same time with 3 sec
1273/// // waiting time.
1274/// #[test_with::lock(ANOTHER_LOCK, 3)]
1275/// fn test_4() {
1276/// assert!(true);
1277/// }
1278///
1279/// }
1280#[proc_macro_attribute]
1281pub fn lock(attr: TokenStream, stream: TokenStream) -> TokenStream {
1282 if is_module(&stream) {
1283 return syn::Error::new(
1284 proc_macro2::Span::call_site(),
1285 "#[test_with::lock] only works with fn",
1286 )
1287 .to_compile_error()
1288 .into();
1289 } else {
1290 lock_macro(attr, parse_macro_input!(stream as ItemFn))
1291 }
1292}
1293
1294/// Run test case when the timezone is expected.
1295/// ```
1296/// #[cfg(test)]
1297/// mod tests {
1298///
1299/// // 0 means UTC
1300/// #[test_with::timezone(0)]
1301/// #[test]
1302/// fn test_works() {
1303/// assert!(true);
1304/// }
1305///
1306/// // UTC is GMT+0
1307/// #[test_with::timezone(UTC)]
1308/// #[test]
1309/// fn test_works_too() {
1310/// assert!(true);
1311/// }
1312///
1313/// // +8 means GMT+8
1314/// #[test_with::timezone(+8)]
1315/// #[test]
1316/// fn test_ignored() {
1317/// panic!("should be ignored")
1318/// }
1319///
1320/// // HKT GMT+8
1321/// #[test_with::timezone(HKT)]
1322/// #[test]
1323/// fn test_ignored_too() {
1324/// panic!("should be ignored")
1325/// }
1326/// }
1327/// ```
1328#[cfg(feature = "timezone")]
1329#[proc_macro_attribute]
1330pub fn timezone(attr: TokenStream, stream: TokenStream) -> TokenStream {
1331 if is_module(&stream) {
1332 mod_macro(
1333 attr,
1334 parse_macro_input!(stream as ItemMod),
1335 timezone::check_tz_condition,
1336 )
1337 } else {
1338 fn_macro(
1339 attr,
1340 parse_macro_input!(stream as ItemFn),
1341 timezone::check_tz_condition,
1342 )
1343 }
1344}
1345
1346/// Run test case when the example running within specific timezones.
1347///```rust
1348/// // write as example in examples/*rs
1349/// test_with::runner!(timezone);
1350/// #[test_with::module]
1351/// mod timezone {
1352/// // 0 means UTC timezone
1353/// #[test_with::runtime_timezone(0)]
1354/// fn test_works() {
1355/// assert!(true);
1356/// }
1357/// }
1358#[cfg(not(feature = "runtime"))]
1359#[proc_macro_attribute]
1360pub fn runtime_timezone(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1361 return syn::Error::new(
1362 proc_macro2::Span::call_site(),
1363 "should be used with runtime feature",
1364 )
1365 .to_compile_error()
1366 .into();
1367}
1368
1369#[cfg(all(feature = "runtime", feature = "timezone"))]
1370#[proc_macro_attribute]
1371pub fn runtime_timezone(attr: TokenStream, stream: TokenStream) -> TokenStream {
1372 timezone::runtime_timezone(attr, stream)
1373}
1374
1375/// Provide a test runner and test on each module
1376///```rust
1377/// // example/run-test.rs
1378///
1379/// test_with::runner!(module1, module2);
1380/// #[test_with::module]
1381/// mod module1 {
1382/// #[test_with::runtime_env(PWD)]
1383/// fn test_works() {
1384/// assert!(true);
1385/// }
1386/// }
1387///
1388/// #[test_with::module]
1389/// mod module2 {
1390/// #[test_with::runtime_env(PWD)]
1391/// fn test_works() {
1392/// assert!(true);
1393/// }
1394/// }
1395///```
1396#[cfg(not(feature = "runtime"))]
1397#[proc_macro]
1398pub fn runner(_input: TokenStream) -> TokenStream {
1399 return syn::Error::new(
1400 proc_macro2::Span::call_site(),
1401 "should be used with runtime feature",
1402 )
1403 .to_compile_error()
1404 .into();
1405}
1406#[cfg(feature = "runtime")]
1407#[proc_macro]
1408pub fn runner(input: TokenStream) -> TokenStream {
1409 runtime::runner(input)
1410}
1411
1412#[cfg(not(feature = "runtime"))]
1413#[proc_macro]
1414pub fn tokio_runner(_input: TokenStream) -> TokenStream {
1415 return syn::Error::new(
1416 proc_macro2::Span::call_site(),
1417 "should be used with `runtime` feature",
1418 )
1419 .to_compile_error()
1420 .into();
1421}
1422#[cfg(feature = "runtime")]
1423#[proc_macro]
1424pub fn tokio_runner(input: TokenStream) -> TokenStream {
1425 runtime::tokio_runner(input)
1426}
1427
1428/// Help each function with `#[test_with::runtime_*]` in the module can register to run
1429/// Also you can set up a mock instance for all of the test in the module
1430///
1431/// ```rust
1432/// // example/run-test.rs
1433///
1434/// test_with::runner!(module1, module2);
1435/// #[test_with::module]
1436/// mod module1 {
1437/// #[test_with::runtime_env(PWD)]
1438/// fn test_works() {
1439/// assert!(true);
1440/// }
1441/// }
1442///
1443/// #[test_with::module]
1444/// mod module2 {
1445/// #[test_with::runtime_env(PWD)]
1446/// fn test_works() {
1447/// assert!(true);
1448/// }
1449/// }
1450/// ```
1451/// You can set up mock with a public struct named `TestEnv` inside the module, or a public type
1452/// named `TestEnv` inside the module. And the type or struct should have a Default trait for
1453/// initialize the mock instance.
1454/// ```rust
1455/// use std::ops::Drop;
1456/// use std::process::{Child, Command};
1457///
1458/// test_with::runner!(net);
1459///
1460/// #[test_with::module]
1461/// mod net {
1462/// pub struct TestEnv {
1463/// p: Child,
1464/// }
1465///
1466/// impl Default for TestEnv {
1467/// fn default() -> TestEnv {
1468/// let p = Command::new("python")
1469/// .args(["-m", "http.server"])
1470/// .spawn()
1471/// .expect("failed to execute child");
1472/// let mut count = 0;
1473/// while count < 3 {
1474/// if test_with::reqwest::blocking::get("http://127.0.0.1:8000").is_ok() {
1475/// break;
1476/// }
1477/// std::thread::sleep(std::time::Duration::from_secs(1));
1478/// count += 1;
1479/// }
1480/// TestEnv { p }
1481/// }
1482/// }
1483///
1484/// impl Drop for TestEnv {
1485/// fn drop(&mut self) {
1486/// self.p.kill().expect("fail to kill python http.server");
1487/// }
1488/// }
1489///
1490/// #[test_with::runtime_http(127.0.0.1:8000)]
1491/// fn test_with_environment() {
1492/// assert!(true);
1493/// }
1494/// }
1495///
1496/// ```
1497/// or you can write mock struct in other place and just pass by type.
1498/// ```rust
1499/// use std::ops::Drop;
1500/// use std::process::{Child, Command};
1501///
1502/// test_with::runner!(net);
1503///
1504/// pub struct Moc {
1505/// p: Child,
1506/// }
1507///
1508/// impl Default for Moc {
1509/// fn default() -> Moc {
1510/// let p = Command::new("python")
1511/// .args(["-m", "http.server"])
1512/// .spawn()
1513/// .expect("failed to execute child");
1514/// let mut count = 0;
1515/// while count < 3 {
1516/// if test_with::reqwest::blocking::get("http://127.0.0.1:8000").is_ok() {
1517/// break;
1518/// }
1519/// std::thread::sleep(std::time::Duration::from_secs(1));
1520/// count += 1;
1521/// }
1522/// Moc { p }
1523/// }
1524/// }
1525///
1526/// impl Drop for Moc {
1527/// fn drop(&mut self) {
1528/// self.p.kill().expect("fail to kill python http.server");
1529/// }
1530/// }
1531///
1532/// #[test_with::module]
1533/// mod net {
1534/// pub type TestEnv = super::Moc;
1535///
1536/// #[test_with::runtime_http(127.0.0.1:8000)]
1537/// fn test_with_environment() {
1538/// assert!(true);
1539/// }
1540/// }
1541/// ```
1542#[cfg(not(feature = "runtime"))]
1543#[proc_macro_attribute]
1544pub fn module(_attr: TokenStream, _stream: TokenStream) -> TokenStream {
1545 panic!("should be used with runtime feature")
1546}
1547#[cfg(feature = "runtime")]
1548#[proc_macro_attribute]
1549pub fn module(attr: TokenStream, stream: TokenStream) -> TokenStream {
1550 runtime::module(attr, stream)
1551}