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-8", 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}