test_with/
lib.rs

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