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