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