Reach

Grow

Manage

Automate

Reach

Grow

Manage

Automate

Memulai dengan CppUTest

Burung

14 Mei 2017

Email

1 min read

Memulai dengan CppUTest

Burung

14 Mei 2017

Email

1 min read

Memulai dengan CppUTest

Di SparkPost, kami menginvestasikan banyak waktu dan usaha untuk menguji kode kami. Platform kami ditulis dalam C, dan baru-baru ini saya meneliti integrasi dengan kerangka pengujian unit yang disebut "CppUTest", yang menyediakan pengujian gaya xUnit untuk C/C++. Kerangka kerja ini kuat, kaya fitur, dan sedang dalam pengembangan aktif, menjadikannya pilihan yang hebat. Ini juga menyediakan lapisan integrasi C yang memudahkan penggunaan dengan kode C platform kami meskipun sebagian besar kerangka kerja ini adalah C++. Tutorial ini mencakup cara memulai dengan CppUTest pada proyek Anda sendiri.

Di SparkPost, kami menghabiskan banyak waktu dan usaha untuk menguji kode kami. Platform kami ditulis dalam C, dan baru-baru ini saya meneliti integrasi dengan kerangka pengujian unit yang disebut “CppUTest”, yang menyediakan pengujian gaya xUnit untuk C/C++. Kerangka ini kuat, kaya fitur, dan sedang dalam pengembangan aktif, yang menjadikannya pilihan yang bagus. Ini juga menyediakan lapisan integrasi C yang membuatnya mudah digunakan dengan kode C platform kami meskipun sebagian besar kerangka kerja adalah C++. Tutorial ini mencakup cara memulai dengan CppUTest pada proyek Anda sendiri.

Mengunduh CppUTest

Halaman proyek CppUTest tersedia di sini, dan repositorinya ada di github. Ini juga termasuk dalam repositori manajemen paket untuk banyak distro linux, serta homebrew di Mac OS. Contoh-contoh yang mengikuti dieksekusi di Mac OS X, tetapi berasal dari kode yang ditulis untuk Red Hat, OS di mana platform kami berjalan.

Dasar-dasarnya didokumentasikan dengan baik di halaman utama CppUTest. Kami akan membahasnya sedikit dan beralih ke beberapa fitur yang lebih menarik.

Membangun Fondasi

Hal pertama yang perlu dilakukan, mari kita tulis beberapa kode!

Proyek pengujian kita akan memiliki file 'main' dan akan menyertakan pustaka utilitas yang disebut 'code'. Pustaka ini akan menyediakan fungsi sederhana yang mengembalikan 1 (untuk saat ini). File-file akan disusun seperti ini:

├── src │ ├── code │ │ ├── code.cpp │ │ └── code.h │ └── main.cpp └── t ├── main.cpp └── test.cpp

Mari kita mulai dengan menulis file src/

// src/main.cpp #include <stdlib.h> #include <stdio.h> #include "code.h" int main(void) { test_func(); printf("hello world!\n"); exit(0); }

// src/code/code.cpp #include <stdlib.h> #include "code.h" int test_func () { return 1; }

// src/code/code.h #ifndef __code_h__ #define __code_h__ int test_func (); #endif

Sekarang, mari kita lakukan pengujian yang akan berada di direktori t/.  Hal pertama yang harus dilakukan adalah menyiapkan test runner yang akan menjalankan file pengujian kita. Ini juga berfungsi sebagai ‘main’  yang akan dieksekusi setelah semua ini dikompilasi:

// t/main.cpp #include "CppUTest/CommandLineTestRunner.h" int main(int ac, char** av) { return CommandLineTestRunner::RunAllTests(ac, av); }

Sekarang kita bisa menulis modul pengujian pertama kita:

// t/test.cpp #include "CppUTest/TestHarness.h" #include "code.h" TEST_GROUP(AwesomeExamples) { }; TEST(AwesomeExamples, FirstExample) { int x = test_func(); CHECK_EQUAL(1, x); }

Selanjutnya, kita perlu menulis makefile.  Kita akan membutuhkan dua: satu untuk file proyek di bawah src/, dan satu untuk pengujian.

Hal pertama yang perlu dilakukan, mari kita tulis beberapa kode!

Proyek pengujian kita akan memiliki file 'main' dan akan menyertakan pustaka utilitas yang disebut 'code'. Pustaka ini akan menyediakan fungsi sederhana yang mengembalikan 1 (untuk saat ini). File-file akan disusun seperti ini:

├── src │ ├── code │ │ ├── code.cpp │ │ └── code.h │ └── main.cpp └── t ├── main.cpp └── test.cpp

Mari kita mulai dengan menulis file src/

// src/main.cpp #include <stdlib.h> #include <stdio.h> #include "code.h" int main(void) { test_func(); printf("hello world!\n"); exit(0); }

// src/code/code.cpp #include <stdlib.h> #include "code.h" int test_func () { return 1; }

// src/code/code.h #ifndef __code_h__ #define __code_h__ int test_func (); #endif

Sekarang, mari kita lakukan pengujian yang akan berada di direktori t/.  Hal pertama yang harus dilakukan adalah menyiapkan test runner yang akan menjalankan file pengujian kita. Ini juga berfungsi sebagai ‘main’  yang akan dieksekusi setelah semua ini dikompilasi:

// t/main.cpp #include "CppUTest/CommandLineTestRunner.h" int main(int ac, char** av) { return CommandLineTestRunner::RunAllTests(ac, av); }

Sekarang kita bisa menulis modul pengujian pertama kita:

// t/test.cpp #include "CppUTest/TestHarness.h" #include "code.h" TEST_GROUP(AwesomeExamples) { }; TEST(AwesomeExamples, FirstExample) { int x = test_func(); CHECK_EQUAL(1, x); }

Selanjutnya, kita perlu menulis makefile.  Kita akan membutuhkan dua: satu untuk file proyek di bawah src/, dan satu untuk pengujian.

Hal pertama yang perlu dilakukan, mari kita tulis beberapa kode!

Proyek pengujian kita akan memiliki file 'main' dan akan menyertakan pustaka utilitas yang disebut 'code'. Pustaka ini akan menyediakan fungsi sederhana yang mengembalikan 1 (untuk saat ini). File-file akan disusun seperti ini:

├── src │ ├── code │ │ ├── code.cpp │ │ └── code.h │ └── main.cpp └── t ├── main.cpp └── test.cpp

Mari kita mulai dengan menulis file src/

// src/main.cpp #include <stdlib.h> #include <stdio.h> #include "code.h" int main(void) { test_func(); printf("hello world!\n"); exit(0); }

// src/code/code.cpp #include <stdlib.h> #include "code.h" int test_func () { return 1; }

// src/code/code.h #ifndef __code_h__ #define __code_h__ int test_func (); #endif

Sekarang, mari kita lakukan pengujian yang akan berada di direktori t/.  Hal pertama yang harus dilakukan adalah menyiapkan test runner yang akan menjalankan file pengujian kita. Ini juga berfungsi sebagai ‘main’  yang akan dieksekusi setelah semua ini dikompilasi:

// t/main.cpp #include "CppUTest/CommandLineTestRunner.h" int main(int ac, char** av) { return CommandLineTestRunner::RunAllTests(ac, av); }

Sekarang kita bisa menulis modul pengujian pertama kita:

// t/test.cpp #include "CppUTest/TestHarness.h" #include "code.h" TEST_GROUP(AwesomeExamples) { }; TEST(AwesomeExamples, FirstExample) { int x = test_func(); CHECK_EQUAL(1, x); }

Selanjutnya, kita perlu menulis makefile.  Kita akan membutuhkan dua: satu untuk file proyek di bawah src/, dan satu untuk pengujian.

Project Makefile

Makefile proyek akan berada pada level yang sama dengan direktori ‘src’ dan ‘t’ di root proyek. Ini akan terlihat seperti ini:

# Makefile SRC_DIR=./src CODE_DIR=$(SRC_DIR)/code OUT=contoh TEST_DIR=t test: make -C $(TEST_DIR) test_clean: make -C $(TEST_DIR) clean code.o: gcc -c -I$(CODE_DIR) $(CODE_DIR)/code.cpp -o $(CODE_DIR)/code.o main: code.o gcc -I$(CODE_DIR) $(CODE_DIR)/code.o $(SRC_DIR)/main.cpp -o $(OUT) all: test main clean: test_clean rm $(SRC_DIR)/*.o $(CODE_DIR)/*.o $(OUT)

Perhatikan bahwa ini menggunakan ‘make -C’ untuk target tes – artinya akan memanggil ‘make’ lagi menggunakan makefile di direktori tes.

Pada titik ini kita dapat mengompilasi kode ‘src’ dengan makefile dan melihat bahwa ini berfungsi:

[]$ make main gcc -c -I./src/code ./src/code/code.cpp -o ./src/code/code.o gcc -I./src/code ./src/code/code.o ./src/main.cpp -o contoh []$ ./contoh hello world!

Tests Makefile

Untuk pengujian, prosesnya sedikit lebih rumit karena kita perlu memuat dan berintegrasi dengan pustaka CppUTest dengan benar.

Repositori CppUTest menyediakan sebuah file yang disebut "MakefileWorker.mk". File ini menyediakan banyak fungsionalitas yang membuat pembangunan dengan CppUTest menjadi sederhana. File tersebut terletak di bawah direktori "build" dalam repositori git. Untuk tutorial ini kita akan mengasumsikan file tersebut telah disalin ke direktori ‘t/’. File ini dapat digunakan sebagai berikut:

# kita tidak ingin menggunakan jalur relatif, jadi kita menetapkan variabel ini PROJECT_DIR=/path/to/project SRC_DIR=$(PROJECT_DIR)/src TEST_DIR=$(PROJECT_DIR)/t # tentukan di mana kode sumber dan includes berada INCLUDE_DIRS=$(SRC_DIR)/code SRC_DIRS=$(SRC_DIR)/code # tentukan di mana kode pengujian berada TEST_SRC_DIRS = $(TEST_DIR) # beri nama pada binary pengujian TEST_TARGET=example # di mana pustaka cpputest berada CPPUTEST_HOME=/usr/local # jalankan MakefileWorker.mk dengan variabel yang didefinisikan di sini include MakefileWorker.mk

Perhatikan bahwa CPPUTEST_HOME harus disetel ke lokasi di mana CppUTest diinstal. Jika Anda telah menginstal paket distro, biasanya ini akan terletak di bawah /usr/local pada sistem linux/mac. Jika Anda telah melakukan checkout repo sendiri, itu ada di mana pun checkout itu berada.

Opsi-opsi ini semua didokumentasikan dalam MakefileWorker.mk.

MakefileWorker.mk juga menambahkan beberapa target makefile, termasuk yang berikut:

  1. all – membangun pengujian yang ditunjukkan oleh makefile

  2. clean – menghapus semua file objek dan file gcov yang dihasilkan untuk pengujian

  3. realclean – menghapus file objek atau gcov apa pun di seluruh pohon direktori

  4. flags – mendata semua flag dikonfigurasi yang digunakan untuk mengkompilasi pengujian

  5. debug – mendata semua file sumber, objek, ketergantungan, dan ‘hal yang harus dibersihkan’

Code Coverage

Pengujian unit tidak akan lengkap tanpa laporan cakupan. Alat pilihan untuk proyek yang menggunakan gcc adalah gcov, tersedia sebagai bagian dari paket standar utilitas gcc. Cpputest terintegrasi dengan mudah dengan gcov, yang perlu Anda lakukan hanya menambahkan baris ini ke dalam makefile:

CPPUTEST_USE_GCOV=Y

Selanjutnya, kita perlu memastikan bahwa skrip filterGcov.sh dari repo ini berada di ‘/scripts/filterGcov.sh’ relatif terhadap di mana pun Anda mengatur ‘CPPUTEST_HOME’. Skrip ini juga perlu memiliki izin eksekusi.

Dalam Makefile contoh, ini akan ditempatkan di ‘/usr/local/scripts/filterGcov.sh’. Jika Anda menjalankan CppUTest dari pengecekan repo, semuanya harus berfungsi tanpa modifikasi.




Dengan itu sudah siap, Anda cukup menjalankan ‘make gcov’ dan analisis akan dihasilkan untuk Anda. Dalam kasus kita, kita perlu ‘make -B’ untuk membangun ulang file objek dengan gcov diaktifkan:

[]$ make -B gcov < keluaran kompilasi > untuk d dalam /Users/ykuperman/code/blogpost/qa/src/code ; do \ FILES=`ls $d/*.c $d/*.cc $d/*.cpp 2> /dev/null` ; \ gcov --object-directory objs/$d $FILES >> gcov_output.txt 2>>gcov_error.txt ; \ done untuk f dalam ; do \ gcov --object-directory objs/$f $f >> gcov_output.txt 2>>gcov_error.txt ; \ selesai /usr/local/scripts/filterGcov.sh gcov_output.txt gcov_error.txt gcov_report.txt contoh.txt cat gcov_report.txt 100.00% /Users/ykuperman/code/blogpost/qa/src/code/code.cpp mkdir -p gcov pindahkan *.gcov gcov pindahkan gcov_* gcov Lihat direktori gcov untuk detail

Ini akan menghasilkan sejumlah file ke dalam direktori ‘gcov’ baru. File-file ini adalah:

  1. code.cpp.gcov – file ‘gcov’ sesungguhnya untuk kode yang diuji

  2. gcov_error.txt – laporan kesalahan (dalam kasus kita, seharusnya kosong)

  3. gcov_output.txt – keluaran sesungguhnya dari perintah gcov yang dijalankan

  4. gcov_report.txt – ringkasan cakupan untuk setiap file yang diuji

  5. gcov_report.txt.html – versi html dari gcov_report

Cpputest Memory Leak Detection

Cpputest memungkinkan Anda mendeteksi kebocoran memori secara otomatis dengan mendefinisikan ulang keluarga fungsi standar “malloc/free” untuk menggunakan pembungkusnya sendiri. Ini memungkinkannya dengan cepat menangkap kebocoran dan melaporkannya untuk setiap eksekusi pengujian. Ini diaktifkan secara default di MakefileWorker.mk, jadi sudah aktif dengan langkah-langkah yang telah diuraikan sejauh ini.

Sebagai ilustrasi, mari kita biarkan beberapa memori bocor di test_func() !

Kembali ke code.c, kita tambahkan malloc()  ke fungsi, seperti ini:

int test_func() { malloc(1); return 1; }

Sekarang, setelah dikompilasi ulang, kesalahan berikut dihasilkan:

test.cpp:9: error: Failure in TEST(AwesomeExamples, FirstExample) Memory leak(s) found. Alloc num (4) Leak size: 1 Allocated at: ./code.c and line: 6. Type: "malloc" Memory: <0x7fc9e94056d0> Content: 0000: 00 |.| Total number of leaks: 1

Ini menunjukkan tes mana yang menyebabkan kebocoran, di mana kebocoran terjadi dalam kode sumber, dan apa yang ada dalam memori yang bocor. Sangat membantu!

Ada beberapa peringatan dengan fitur ini:

  1. Cpputest menggunakan makro preprocessor untuk mendefinisikan ulang secara dinamis semua panggilan ke fungsi manajemen memori standar. Itu berarti hanya akan berfungsi untuk panggilan dalam kode sumber yang diuji karena itulah yang dikompilasi dengan penggantian CppUTest. Kebocoran di perpustakaan terkait tidak akan terdeteksi.

  2. Terkadang memori yang dialokasikan untuk seluruh proses tidak dimaksudkan untuk dibebaskan. Ini dapat membuat banyak pesan kesalahan yang mengganggu jika Anda menguji modul dengan perilaku ini. Untuk menonaktifkan deteksi kebocoran, Anda dapat melakukan ini:

CPPUTEST_USE_MEM_LEAK_DETECTION=N

Tertarik pada More?

Ini hanyalah puncak gunung es ketika datang ke semua fitur yang terkandung dalam alat ini. Selain dasar-dasar yang dibahas di sini, alat ini juga memiliki kerangka kerja mocking, lapisan integrasi C langsung, dan kerangka kerja plugin, untuk menyebutkan beberapa yang signifikan. Repo ini juga berisi seluruh direktori skrip pembantu yang dapat membantu mengotomatisasi beberapa bagian rutin dari bekerja dengan kerangka kerja tersebut.

Saya berharap informasi di sini membantu Anda meningkatkan kualitas kode C/C++ Anda dengan alat yang hebat ini!

Mari hubungkan Anda dengan pakar Bird.
Lihat kekuatan penuh dari Bird dalam 30 menit.

Dengan mengirimkan, Anda setuju Bird dapat menghubungi Anda tentang produk dan layanan kami.

Anda dapat berhenti berlangganan kapan saja. Lihat Pernyataan Privasi Bird untuk detail tentang pemrosesan data.

Perusahaan

Newsletter

Tetap terinformasi dengan Bird melalui pembaruan mingguan ke kotak masuk Anda.

Mari hubungkan Anda dengan pakar Bird.
Lihat kekuatan penuh dari Bird dalam 30 menit.

Dengan mengirimkan, Anda setuju Bird dapat menghubungi Anda tentang produk dan layanan kami.

Anda dapat berhenti berlangganan kapan saja. Lihat Pernyataan Privasi Bird untuk detail tentang pemrosesan data.

Perusahaan

Newsletter

Tetap terinformasi dengan Bird melalui pembaruan mingguan ke kotak masuk Anda.

Mari hubungkan Anda dengan pakar Bird.
Lihat kekuatan penuh dari Bird dalam 30 menit.

Dengan mengirimkan, Anda setuju Bird dapat menghubungi Anda tentang produk dan layanan kami.

Anda dapat berhenti berlangganan kapan saja. Lihat Pernyataan Privasi Bird untuk detail tentang pemrosesan data.

R

Reach

G

Grow

M

Manage

A

Automate

Perusahaan

Newsletter

Tetap terinformasi dengan Bird melalui pembaruan mingguan ke kotak masuk Anda.