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