.\" This manpage has been automatically generated by docbook2man .\" from a DocBook document. This tool can be found at: .\" .\" Please send any bug reports, improvements, comments, patches, .\" etc. to Steve Cheng . .TH "CHECKMK" "1" "09 February 2010" "" "" .SH "نام (NAME)" checkmk \- اسکریپت Awk برای تولید تست‌های واحد C جهت استفاده با چارچوب آزمون واحد Check .SH "خلاصه دستور (SYNOPSIS)" \fBcheckmk\fR [ \fBclean_mode=1\fR ] [ \fB\fIinput-file\fB\fR ] .SH "توضیحات (DESCRIPTION)" .PP تولید فایل‌های منبع زبان C شامل تست‌های واحد برای استفاده با چارچوب آزمون واحد Check. هدف این اسکریپت، خودکارسازی بخشی از کدهای تکراری مرسوم (boilerplate) است که هنگام نوشتن یک مجموعه آزمون با استفاده از Check باید نوشته شوند؛ به ویژه نمونه‌سازی از SRunner، Suite(ها) و TCase(ها)، و برقراری ارتباط بین این اشیاء و توابع تست. .PP این ابزار برای کسانی طراحی شده است که با چارچوب آزمون واحد Check آشنایی دارند. در سرتاسر این راهنما، فرض بر این است که با این چارچوب آشنا هستید. .PP چارچوب Check همراه با اطلاعات مربوط به آن در در دسترس است. .PP آرگومان \fIinput-file\fR در \fBcheckmk\fR از یک ساختار نحوی ساده و شبیه به پیش‌پردازنده C برای اعلان توابع تست و توصیف روابط آن‌ها با Suites و TCases در Check استفاده می‌کند. سپس \fBcheckmk\fR از این اطلاعات برای نوشتن خودکار یک تابع \fBmain()\fR شامل تمامی تعاریف لازم و کدهای مورد نیاز جهت اجرای مجموعه‌های آزمون بهره می‌برد. خروجی نهایی زبان C در خروجی استاندارد \fBcheckmk\fR چاپ می‌شود. .PP امکاناتی برای درج کدهای کاربر در تابع \fBmain()\fR تولیدشده فراهم شده است تا استفاده از ثبت گزارش (logging)، فیکسچرهای آزمون (test fixtures) یا مقادیر خروج سفارشی امکان‌پذیر باشد. .PP اگرچه می‌توان آرگومان \fIinput-file\fR را در \fBcheckmk\fR حذف کرد و فایل ورودی را به‌جای آن از طریق ورودی استاندارد \fBcheckmk\fR ارائه داد، اما عموماً توصیه می‌شود که آن را به عنوان یک آرگومان مشخص کنید. این کار به \fBcheckmk\fR امکان می‌دهد تا از نام فایل مطلع شود، ارجاعاتی به آن در توضیحات ابتدایی خروجی زبان C قرار دهد و دستورالعمل‌های C #line را در سرتاسر کد درج کند تا با هدایت کاربر به فایل ورودی اصلی، اشکال‌زدایی خطاها تسهیل شود. .SH "گزینه‌ها (OPTIONS)" .PP تنها گزینه رسماً پشتیبانی‌شده، تعیین یک مقدار درست (بر اساس تعریف Awk برای «درست») برای متغیر \fBclean_mode\fR است. این کار باعث می‌شود \fBcheckmk\fR دستورالعمل‌های مناسب #line را در کد منبع قرار ندهد، که ممکن است از نظر برخی افراد شلوغی غیرضروری به نظر برسد. .PP نویسنده استفاده از این گزینه را توصیه نمی‌کند، چرا که باعث می‌شود کامپایلرهای C و ابزارهای اشکال‌زدایی به‌جای ارجاع به فایل‌های ورودی اصلی \fBcheckmk\fR، به خطوط خروجی تولیدشده به‌صورت خودکار ارجاع دهند. این موضوع کاربران را تشویق می‌کند تا به‌جای فایل‌های ورودی اصلی، فایل‌های خروجی را ویرایش کنند؛ این امر نمایش فایل مناسب جهت ویرایش را برای ویرایشگرهای هوشمند یا محیط‌های توسعه یکپارچه (IDE) دشوار می‌سازد و ممکن است هنگام تولید مجدد فایل‌های خروجی، اصلاحات انجام‌شده بازنویسی شوند. .PP دستورالعمل‌های #line هنگامی که فایل ورودی به جای آرگومان خط فرمان، از طریق ورودی استاندارد ارائه شود، به‌طور خودکار غیرفعال می‌شوند. .SH "مثال پایه (BASIC EXAMPLE)" .PP در ساده‌ترین حالت، یک فایل ورودی می‌تواند صرفاً شامل یک مقدمه (prologue) و یک تابع آزمون باشد. هر چیزی که پیش از اولین تابع آزمون قرار گیرد در بخش مقدمه خواهد بود و عیناً در خروجی کپی می‌شود. تابع آزمون با خطی به این شکل آغاز می‌شود: .nf #test \fItest_name\fR .fi .PP که در آن \fItest_name\fR نام تابع آزمون شما است. این نام برای نام‌گذاری یک تابع C استفاده خواهد شد، بنابراین باید یک شناسه معتبر در C باشد. .PP در اینجا یک مثال کوچک و کامل آورده شده است: .nf -------------------------------------------------- /* A complete test example */ #include #test the_test int nc; const char msg[] = "\\n\\n Hello, world!\\n"; nc = printf("%s", msg); ck_assert(nc == (sizeof(msg) - 1)); /* for terminating NUL. */ -------------------------------------------------- .fi .PP اگر موارد بالا را در فایلی به نام \fIbasic_complete.ts\fR قرار دهید و آن را با دستور زیر پردازش کنید: .PP \fB$ checkmk basic_complete.ts > basic_complete.c\fR .PP فایل \fIbasic_complete.c\fR حاوی خروجی مشابه زیر خواهد بود: .nf -------------------------------------------------- /* * DO NOT EDIT THIS FILE. Generated by checkmk. * Edit the original source file "in" instead. */ #include /* A complete test example */ #include START_TEST(the_test) { int nc; const char msg[] = "\\n\\n Hello, world!\\n"; nc = printf("%s", msg); ck_assert(nc == (sizeof(msg) - 1)); /* for terminating NUL. */ } END_TEST int main(void) { Suite *s1 = suite_create("Core"); TCase *tc1_1 = tcase_create("Core"); SRunner *sr = srunner_create(s1); int nf; suite_add_tcase(s1, tc1_1); tcase_add_test(tc1_1, the_test); srunner_run_all(sr, CK_ENV); nf = srunner_ntests_failed(sr); srunner_free(sr); return nf == 0 ? 0 : 1; } -------------------------------------------------- .fi .PP در کاربرد واقعی، \fIbasic_complete.c\fR شامل دستورالعمل‌های #line نیز خواهد بود..SH "خلاصه دستورالعمل‌ها (DIRECTIVE SUMMARY)" .PP در اینجا خلاصه کاملی از تمام دستورالعمل‌های سبک پیش‌پردازنده C آورده شده است که توسط \fBcheckmk\fR\& فهمیده می‌شوند. برای جزئیات بیشتر به ادامه مطلب مراجعه کنید. .nf # test \fItest_name\fR # test-signal(\fIsignal\fR) \fItest_name\fR # test-exit(\fIexit_code\fR) \fItest_name\fR # test-loop(\fIstart\fR, \fIend\fR) \fItest_name\fR # test-loop-signal(\fIsignal\fR, \fIstart\fR, \fIend\fR) \fItest_name\fR # test-loop-exit(\fIexit_code\fR, \fIstart\fR, \fIend\fR) \fItest_name\fR # suite \fITestSuiteName\fR # tcase \fITestCaseName\fR # main-pre # main-post .fi .PP تمام دستورالعمل‌ها به بزرگی و کوچکی حروف حساس نیستند (case-insensitive). فاصله‌های خالی می‌توانند در ابتدای خط پیش از #، بین # و دستورالعمل، بین دستورالعمل و هر آرگومان، و در انتهای خط قرار گیرند. .SH "دستورالعمل‌های تعریف آزمون (TEST-DEFINING DIRECTIVES)" .PP در اینجا توضیحات دقیق‌تری در مورد دستورالعمل‌هایی ارائه شده است که می‌توانند برای تعریف توابع آزمون و دربرگیرنده‌های آن‌ها استفاده شوند. .SS "توابع آزمون (TEST FUNCTIONS)" .nf # test \fItest_name\fR # test-signal(\fIsignal\fR) \fItest_name\fR # test-exit(\fIexit_code\fR) \fItest_name\fR # test-loop(\fIstart\fR, \fIend\fR) \fItest_name\fR # test-loop-signal(\fIsignal\fR, \fIstart\fR, \fIend\fR) \fItest_name\fR # test-loop-exit(\fIexit_code\fR, \fIstart\fR, \fIend\fR) \fItest_name\fR .fi .PP این‌ها پایه‌ای‌ترین دستورالعمل‌ها برای ایجاد یک قالب ورودی به \fBcheckmk\fR\& هستند. آن‌ها تنها دستورالعمل‌های اجباری می‌باشند: باید حداقل یک دستورالعمل #test* در قالب وجود داشته باشد، در غیر این صورت \fBcheckmk\fR با یک پیام خطا با شکست مواجه خواهد شد. دستورالعمل‌های #test* می‌توانند چندین بار مشخص شوند که هر کدام آغازگر تعریف یک تابع آزمون جدید است. .PP آرگومان \fItest_name\fR به عنوان نام تابع آزمون در خروجی به زبان C استفاده خواهد شد، بنابراین باید یک شناسه معتبر C باشد. به این معنی که باید با یک نویسه الفبایی یا زیرخط (_) آغاز شود، و به دنبال آن نویسه‌های اختیاری الفبایی\-عددی و/یا زیرخط قرار گیرند. .PP نام‌های نویسه عمومی (معرفی‌شده در C99) نیز به فرم \\uXXXX یا \\UXXXXXXXX مجاز هستند، که در آن‌ها Xها نشان‌دهنده ارقام هگزادسیمال می‌باشند. .PP مشخص کردن یک \fItest_name\fR یکسان در بیش از یک دستورالعمل #test* یک خطا محسوب می‌شود، بدون در نظر گرفتن این‌که آیا به موردها یا مجموعه‌های آزمون متفاوتی مرتبط هستند یا خیر. .PP برای دیدن فهرست شناسه‌هایی که نباید به عنوان نام تابع آزمون استفاده شوند، به بخش شناسه‌های CHECKMK (CHECKMK IDENTIFIERS) مراجعه کنید. .SS "مجموعه‌های آزمون (TEST SUITES)" .nf # suite \fITestSuiteName\fR .fi .PP این دستورالعمل نام مجموعه آزمون (شیء \fBSuite\fR در چارچوب آزمون Check) را مشخص می‌کند که تمام موردهای آزمون بعدی (و توابع آزمون آن‌ها) به آن افزوده خواهند شد. .PP مقدار \fITestSuiteName\fR یک رشته متنی است و می‌تواند شامل هر نوع نویسه‌ای باشد (به جز نویسه ASCII NUL و خط جدید (newline) که دستورالعمل را پایان می‌دهد). هرگونه فاصله خالی در ابتدا یا انتهای نام مجموعه آزمون نادیده گرفته خواهد شد. .PP آغاز یک مجموعه آزمون جدید همچنین یک مورد آزمون جدید را شروع می‌کند که نام آن با مجموعه آزمون جدید یکسان است. این نام مورد آزمون می‌تواند توسط یک دستورالعمل بعدی #tcase بازنویسی شود. .PP توجه داشته باشید که یک شیء \fBSuite\fR در خروجی C توسط \fBcheckmk\fR در عمل تعریف نخواهد شد، مگر اینکه در نقطه‌ای پس از آن یک دستورالعمل #test (بدون قرار گرفتن یک #suite در این بین) آمده باشد. نداشتن هیچ #test مرتبطی برای یک #suite خطا محسوب نمی‌شود؛ در این حالت #suite (و هر #tcase مرتبط با آن) به‌سادگی هیچ عملیاتی را از جانب \fBcheckmk\fR در پی نخواهند داشت (و در نتیجه بی‌فایده خواهند بود). .PP مشخص کردن چندباره یک مجموعه آزمون یکسان (حساس به بزرگی و کوچکی حروف) توسط دستورالعمل #suite خطا است، مگر اینکه استفاده‌های قبلی به دلیل نبود حداقل یک دستورالعمل مرتبط #test نمونه‌سازی (instantiated) نشده باشند. .PP اگر قبل از اولین دستورالعمل #test یک دستورالعمل #suite را مشخص نکنید، \fBcheckmk\fR معادل یک دستورالعمل ضمنی #suite با رشته "Core" به عنوان مقدار \fITestSuiteName\fR عمل می‌کند (این موضوع همچنین نشان‌دهنده یک شیء مورد آزمون "Core" است). این مورد در بالا در بخش مثال پایه (BASIC EXAMPLE) نشان داده شده است. .SS "موردهای آزمون (TEST CASES)" .nf # tcase \fITestCaseName\fR .fi .PP این دستورالعمل نام مورد آزمون (شیء \fBTCase\fR در چارچوب آزمون Check) را مشخص می‌کند که تمام توابع آزمون بعدی به آن افزوده خواهند شد. .PP دستورالعمل #tcase به شیوه‌ای بسیار شبیه به #suite\& عمل می‌کند. مقدار \fITestCaseName\fR یک رشته متنی است و می‌تواند شامل نویسه‌های دلخواه باشد؛ و یک شیء \fBTCase\fR در عمل تعریف نخواهد شد مگر اینکه به دنبال آن یک دستورالعمل مرتبط #test بیاید. .PP مشخص کردن چندباره یک مورد آزمون یکسان (حساس به بزرگی و کوچکی حروف) توسط دستورالعمل #tcase خطا است، مگر اینکه استفاده‌های قبلی به دلیل نبود حداقل یک دستورالعمل مرتبط #test نمونه‌سازی نشده باشند. .PP همچنین به دستورالعمل #suite که در بالا شرح داده شد مراجعه کنید..SH "کد کاربر در MAIN() (USER CODE IN MAIN())" .PP تابع \fBmain()\fR در زبان C به‌طور خودکار توسط \fBcheckmk\fR تولید می‌شود و \fBSRunner\fRها، \fBSuite\fRها و\~\fBTCase\fRهای لازم را که توسط دستورالعمل‌های تعریف آزمون مشخص‌شده از سوی کاربر نیاز است، تعریف می‌کند. .PP در بیشتر شرایط، این \fBmain()\fR کاملاً خودکار به همین شکل بسیار مناسب است. با این حال، موقعیت‌هایی وجود دارد که ممکن است فرد بخواهد کد سفارشی به \fBmain()\fR اضافه کند. برای مثال، اگر کاربر بخواهد: .TP 0.2i \(bu مقدار مهلت زمانی (timeout) آزمون را از طریق \fBtcase_set_timeout()\fR تغییر دهد، .TP 0.2i \(bu حالت «عدم فورک» (no-fork-mode) ابزار Check را از طریق \fBsrunner_set_fork_status()\fR مشخص کند، .TP 0.2i \(bu فیکسچرهای آزمون را برای برخی از موارد آزمون از طریق \fBtcase_add_checked_fixture()\fR یا\~\fBtcase_add_unchecked_fixture()\fR برپا کند، .TP 0.2i \(bu ثبت وقایع آزمون (logging) را برای اجراکنندهٔ مجموعه (suite runner) از طریق \fBsrunner_set_log()\fR یا\~\fBsrunner_set_xml()\fR برپا کند، یا .TP 0.2i \(bu پس از اجرای مجموعه‌های آزمون، عملیات پاک‌سازی و جمع‌بندی سفارشی را انجام دهد. .PP برای این اهداف، دستورالعمل‌های #main-pre و\~#main-post ارائه شده‌اند. .SS "مقدمه MAIN() (MAIN() PROLOGUE)" .nf # main-pre .fi .PP متن پس از این دستورالعمل، عیناً در بدنهٔ تابع تولیدشدهٔ \fBmain()\fR، درست پس از اعلان متغیرهای محلی خود \fBcheckmk\fR و پیش از انجام هرگونه اجرای آزمون قرار خواهد گرفت (در واقع، حتی قبل از اینکه روابط بین آزمون‌ها، موارد آزمون و مجموعه‌های آزمون برقرار شود، هرچند این موضوع نباید تفاوت چندانی ایجاد کند). از آنجا که \fBcheckmk\fR تازه اعلان‌های خود را به پایان رسانده است، حتی طبق رهنمودهای سخت‌گیرانهٔ ISO C 1990 نیز مجاز است که اعلان‌های متغیر سفارشی در اینجا قرار گیرند. .PP برخلاف دستورالعمل‌های توصیف‌شدهٔ قبلی، #main-pre حداکثر یک‌بار می‌تواند مشخص شود. نباید قبل از آن دستورالعمل #main-post قرار گیرد و هیچ دستورالعمل #suite، #tcase یا #test نمی‌تواند پس از آن ظاهر شود. .PP دستورالعمل #main-pre مکان مناسبی برای تغییر تنظیمات یا برپاسازی فیکسچرهای آزمون است. البته برای انجام این کار، باید بدانید \fBcheckmk\fR از چه نام‌هایی برای نمونه‌سازی \fBSRunner\fRها، \fBSuite\fRها و\~\fBTCase\fRها استفاده کرده است. .SS "شناسه‌های CHECKMK (CHECKMK IDENTIFIERS)" .PP اشاره‌گرها به \fBSuite\fRها با استفاده از الگوی s\fIX\fR اعلان می‌شوند، که در آن \fIX\fR عددی است که از ۱ شروع می‌شود و برای هر دستورالعمل #suite بعدی افزایش می‌یابد. s1 همیشه وجود دارد و شامل تابع آزمونی است که توسط اولین دستورالعمل #test اعلان شده است. اگر قبل از آن دستورالعمل یک #suite نیامده باشد، نام "Core" به آن داده خواهد شد. .PP اشاره‌گرها به \fBTCase\fRها با استفاده از الگوی tc\fIX\fR_\fIY\fR اعلان می‌شوند، که در آن \fIX\fR با شمارهٔ استفاده‌شده برای نام \fBSuite\fR که این \fBTCase\fR را در بر دارد مطابقت می‌کند؛ و \fIY\fR عددی است که برای هر \fBSuite\fR جدید از ۱ آغاز می‌شود و برای هر \fBTCase\fR در آن \fBSuite\fR افزایش می‌یابد. .PP اشاره‌گری به \fBSRunner\fR با شناسهٔ sr اعلان می‌شود؛ همچنین یک عدد صحیح به نام nf وجود دارد که تعداد شکست‌های آزمون را (پس از اجرای آزمون‌ها) در خود نگه می‌دارد. .PP به دلایل واضح، کاربر نباید سعی کند شناسه‌های محلی در \fBmain()\fR اعلان کند، یا ماکروها یا توابع آزمونی را تعریف کند که نام آن‌ها ممکن است با نام متغیرهای محلی مورد استفاده توسط \fBcheckmk\fR تداخل داشته باشد. به‌طور خلاصه، این نام‌ها عبارتند از: s\fIX\fR tc\fIX\fR_\fIY\fR sr nf\&. .SS "مؤخره MAIN() (MAIN() EPILOGUE)" .nf # main-post .fi .PP اگرچه به آن اندازه مفید نیست، اما \fBcheckmk\fR همچنین یک دستورالعمل #main-post برای درج کد سفارشی در انتهای \fBmain()\fR، پس از اجرای آزمون‌ها، ارائه می‌دهد. از این دستورالعمل می‌توان برای آزادسازی منابعی که در بخش مقدمه تخصیص یافته‌اند، چاپ اطلاعات دربارهٔ آزمون‌های ناموفق، یا ارائهٔ یک کد وضعیت خروج سفارشی استفاده کرد. .PP توجه داشته باشید که اگر از این دستورالعمل استفاده کنید، \fBcheckmk\fR دستور return را ارائه \fBنخواهد داد\fR: خودتان باید یکی ارائه دهید. .PP دستورالعمل #main-post نباید با هیچ دستورالعمل دیگری که توسط \fBcheckmk\fR شناخته شده است دنبال شود..SH "مثال جامع (COMPREHENSIVE EXAMPLE)" .PP اکنون که با توضیحات تفصیلی رهنمودهای گوناگون آشنا شده‌اید، بیایید عملکرد همهٔ آن‌ها را در قالب این الگوی نسبتاً جامع ببینیم. .nf -------------------------------------------------- #include "mempool.h" /* defines MEMPOOLSZ, prototypes for mempool_init() and mempool_free() */ void *mempool; void mp_setup(void) { mempool = mempool_init(MEMPOOLSZ); ck_assert_msg(mempool != NULL, "Couldn't allocate mempool."); } void mp_teardown(void) { mempool_free(mempool); } /* end of prologue */ #suite Mempool #tcase MP Init #test mempool_init_zero_test mempool = mempool_init(0); ck_assert_msg(mempool == NULL, "Allocated a zero-sized mempool!"); ck_assert_msg(mempool_error(), "Didn't get an error for zero alloc."); /* "MP Util" TCase uses checked fixture. */ #tcase MP Util #test mempool_copy_test void *cp = mempool_copy(mempool); ck_assert_msg(cp != NULL, "Couldn't perform mempool copy."); ck_assert_msg(cp != mempool, "Copy returned original pointer!"); #test mempool_size_test ck_assert(mempool_getsize(mempool) == MEMPOOLSZ); #main-pre tcase_add_checked_fixture(tc1_2, mp_setup, mp_teardown); srunner_set_log(sr, "mplog.txt"); #main-post if (nf != 0) { printf("Hey, something's wrong! %d whole tests failed!\\n", nf); } return 0; /* Harness checks for output, always return success regardless. */ -------------------------------------------------- .fi .PP با دادن این ورودی به \fBcheckmk\fR، خروجی تقریباً به صورت زیر حاصل خواهد شد: .nf -------------------------------------------------- /* * DO NOT EDIT THIS FILE. Generated by checkmk. * Edit the original source file "comprehensive.ts" instead. */ #include #include "mempool.h" void *mempool; void mp_setup(void) { \&... } void mp_teardown(void) { \&... } /* end of prologue */ START_TEST(mempool_init_zero_test) { \&... } END_TEST START_TEST(mempool_copy_test) { \&... } END_TEST START_TEST(mempool_size_test) { \&... } END_TEST int main(void) { Suite *s1 = suite_create("Mempool"); TCase *tc1_1 = tcase_create("MP Init"); TCase *tc1_2 = tcase_create("MP Util"); SRunner *sr = srunner_create(s1); int nf; /* User-specified pre-run code */ tcase_add_checked_fixture(tc1_2, mp_setup, mp_teardown); srunner_set_log(sr, "mplog.txt"); suite_add_tcase(s1, tc1_1); tcase_add_test(tc1_1, mempool_init_zero_test); suite_add_tcase(s1, tc1_2); tcase_add_test(tc1_2, mempool_copy_test); tcase_add_test(tc1_2, mempool_size_test); srunner_run_all(sr, CK_ENV); nf = srunner_ntests_failed(sr); srunner_free(sr); /* User-specified post-run code */ if (nf != 0) { printf("Hey, something's wrong! %d whole tests failed!\\n", nf); } return 0; /* Harness checks for output, always return success regardless. */ } -------------------------------------------------- .fi.SH "نویسنده (AUTHOR)" .PP \fBcheckmk\fR و این راهنما توسط Micah J Cowan نوشته شده‌اند. .PP حق نشر (C) 2006, 2010 Micah J Cowan.