Skip to main content

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}