Evet sevgili dostlar kısa bir yazı ile maceraya devam. Son yazımızda, SDL3 örneğimize birim testleri ve birim test kapsama analizimizi eklemiştik. Bu noktada Cmake ile Ctest konusuna değinmemiştik, bu yazımda Cteste değineceğiz.
Ctest, temelde cmake tarafından, farklı birim test kütüphaneleri kullanarak C++ uygulamalarınız için geliştirmiş olduğunuz birim test uygulamalarını çalıştırmak için kullanılır. Cteste yönelik, öne çıkan hususları aşağıdaki gibi sıralayabiliriz:
- Birim test frameworklerine bağımlılığının olmamasıdır. Google test, Catch2 ya da Boost.Test ile kullanılabilir,
- Test uygulamalarını sistematik bir şekilde çalıştırma ve istediğiniz formatta çıktıları elde edebilmesi, test raporlama,
- Testleri etiketleyerek gruplandırma,
- Başarız olan testlerin tekrar çalıştırılabilmesi ya da testlere yönelik zaman aşımı kontrolü,
- Testlerin paralel bir şekilde koşturulması,
- CI/CD entegrasyonunu kolaylaştırma (buna önümüzdeki yazımda ayrıca değineceğim),
- Bu test koşumlarının, platform bağımsız bir şekilde yapılabilmesidir.
Evet şimdi de, Ctest’i uygulamalarınıza cmake betikleri aracılığı ile nasıl ekleyebileceğimize göz atalım. Aslına bakarsanız birçok bileşeni, bir önceki yazım ile zaten ekledik ama konu bütünlüğü amacı ile burada onlara değineceğim:
- Öncelikli olarak Cteste yönelik kabiliyetleri aktifleştirmek için;
- enable_testing() cmake betiği içerisinde çağrılmalıdır,
- Google test kullandığımız için buna yönelik eklentileri yapmalıyız ki bir önceki yazımda buna değindim. Burada ilaveten bahsetmemiz gereken konu, ilgili test target’ını kullanarak testleri keşfettirme, bunun için de aşağıdaki satırları ekliyoruz. Bu satırlar ile birlikte, ctest ilgili testleri otomatik tespit eder. Ctest en hızlı kullanıma alma yöntemi aslında budur.
- include(GoogleTest)
- gtest_discover_tests(${TEST_TARGET_NAME})
- Yukarıdaki iki satır ile artık birim testlerinizi (google test ile yazılmış) cteste sunabileceksiniz.
- Bir önceki adımdaki yöntemin yanında, el ile testleri tanımlatabiliyoruz. Bunun için öncelikle aşağıdaki satırları her bir test grubu için eklemliyiz:
- add_test(TEST_NAME MyTest COMMAND ${TEST_TARGET_NAME} –gtest_filter=”test filtresi)
- Artık ctest, sadece yukarıdaki komut ile eklediğiniz testleri görebilecek,
- Bunun yanında yukarıdaki komutlar ile eklediğiniz testlere yönelik etiket de ekleyebiliyorsunuz ki bu google’ın kendi filtreleri yanında Ctest ile de filtreleme yapabileceksiniz. Bunun için aşağıdaki komutu kullanabilirsiniz:
-
set_tests_properties(TEST_NAME PROPERTIES LABELS “sdl_resource;creation”)
- Ayrıca, her cpp dosyası için, teste yönelik komutları kolay ve otomatik bir şekilde eklemeye yönelik aşağıdaki gibi bir fonksiyon da işinizi görecektir. Bu fonksiyon, her bir test için ayrı bir çalıştırılabilir dosya oluşturur ve test ekler. Detaylar için https://cfd.university/learn/automating-cfd-solver-and-library-compilation-using-cmake/how-to-automate-testing-with-cmake-ctest-and-gtest/ sayfasına göz atabilirsiniz.
Bu adımlar ile temel ctest ayarlamalarını tamamlamış oluyoruz. Test uygulamasını oluşturup, sonrasında testleri koşturmak için aşağıdaki komutları (tepe cmake betiğinin bulunduğu dizinden itibaren) çağırmanız yeterli:
1 2 3 4 |
cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug cmake --build build cd build/test ctest --output-on-failure |
Yukarıdaki komutları uygulamamızda çalıştırdığınızda aşağıdaki gibi bir çıktı görmeniz gerekiyor:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
❯ ctest --output-on-failure Test project /home/yazilimperver/repo/cpp-playground/build/test Start 1: SDLResourceTest.ConstructorShouldStoreGivenResource 1/8 Test #1: SDLResourceTest.ConstructorShouldStoreGivenResource ................................ Passed 0.01 sec Start 2: SDLResourceTest.DestructorShouldCallDeleterWhenResourceIsValid 2/8 Test #2: SDLResourceTest.DestructorShouldCallDeleterWhenResourceIsValid ..................... Passed 0.00 sec Start 3: SDLResourceTest.DestructorShouldNotCallDeleterWhenResourceIsNull 3/8 Test #3: SDLResourceTest.DestructorShouldNotCallDeleterWhenResourceIsNull ................... Passed 0.00 sec Start 4: SDLResourceTest.MoveConstructorShouldTransferOwnership 4/8 Test #4: SDLResourceTest.MoveConstructorShouldTransferOwnership ............................. Passed 0.00 sec Start 5: SDLResourceTest.MoveAssignmentShouldTransferOwnershipAndCallDeleterOnOldResource 5/8 Test #5: SDLResourceTest.MoveAssignmentShouldTransferOwnershipAndCallDeleterOnOldResource ... Passed 0.00 sec Start 6: SDLResourceTest.MoveAssignmentShouldHandleSelfAssignmentGracefully 6/8 Test #6: SDLResourceTest.MoveAssignmentShouldHandleSelfAssignmentGracefully ................. Passed 0.00 sec Start 7: SDLResourceTest.ReleaseShouldReturnAndClearInternalPointer 7/8 Test #7: SDLResourceTest.ReleaseShouldReturnAndClearInternalPointer ......................... Passed 0.00 sec Start 8: SDLResourceTest.BoolConversionShouldReflectValidity 8/8 Test #8: SDLResourceTest.BoolConversionShouldReflectValidity ................................ Passed 0.00 sec 100% tests passed, 0 tests failed out of 8 Total Test time (real) = 0.05 sec |
Yukarıdaki komutların yanında, sizlere fayda sağlayacak ve ctest’in diğer kabiliyetlerini kullanmanıza olanak sağlayacak diğer ctest komutlarını da aşağıya ekliyorum:
- ctest -V
- Testlere yönelik ayrıntılı çıktıları elde etmenizi sağlar,
- ctest -L TestGrubu
- “TestGrubu” olarak etiketlenmiş test grubunu çalıştırır. Bunu nasıl tanımlayabileceğinize yukarıda değindim,
- ctest –rerun-failed
- Sadece başarısız olan testleri yeniden çalıştır. Bu kabiliyet özellikle CI/CD aşamasında ve flaky testler için faydalı bir kabiliyet,
- ctest -j4
- 4 paralel test çalıştır. Yukarıda ctest için tanımladığınız testleri ayrı processlerde paralel olarak çalıştırmanıza olanak sağlar,
- Özellikle adet olarak fazla olan testleriniz için harika bir kabilyet,
- ctest -R <regex>
- regex ile uyumlu testleri çalıştırır.
Yazımı sonlandırmadan önce etiketlemeye dair, uygulamamız üzerinden bir örnek vermek istiyorum. Bir diğer ifade ile aslında otomatik olarak ctest’in testleri keşfetmesi yerine bunları biz vereceğiz. Bunlara yönelik öncelikle testleri ekliyoruz. Bunlar aslında tek bir test olarak değilde, test grubu olarak da düşünebilirsiniz (bütün testleri eklemediğimin farkındayım, çok uzatmamak adına bu şekilde yaptım):
1 2 3 |
add_test(NAME SDLResourceTest.ConstructorShouldStoreGivenResource COMMAND ${TEST_TARGET_NAME} --gtest_filter=SDLResourceTest.ConstructorShouldStoreGivenResource) add_test(NAME SDLResourceTest.DestructorShouldCallDeleterWhenResourceIsValid COMMAND ${TEST_TARGET_NAME} --gtest_filter=SDLResourceTest.DestructorShouldCallDeleterWhenResourceIsValid) add_test(NAME SDLResourceTest.MoveAssignmentShouldTransferOwnershipAndCallDeleterOnOldResource COMMAND ${TEST_TARGET_NAME} --gtest_filter=SDLResourceTest.MoveAssignmentShouldTransferOwnershipAndCallDeleterOnOldResource) |
Daha sonra, yukarıda belirlediğimiz testlere yönelik etiketleri, aşağıda gösterilen şekilde tanımlıyoruz:
1 2 3 4 |
# Etiketleme set_tests_properties(SDLResourceTest.ConstructorShouldStoreGivenResource PROPERTIES LABELS "sdl_resource;creation") set_tests_properties(SDLResourceTest.DestructorShouldCallDeleterWhenResourceIsValid PROPERTIES LABELS "sdl_resource;destruction") set_tests_properties(SDLResourceTest.MoveAssignmentShouldTransferOwnershipAndCallDeleterOnOldResource PROPERTIES LABELS "sdl_resource;move") |
gördüğünüz gibi, her test için aslında birden fazla etiket verebiliyoruz. Son olarak da, ilgili etikete sahip testleri koşturmak için de ilgili etiketleri, ctest komutuna veriyoruz. Bunları verirken “|” ile testleri “or” layabiliyoruz.
1 |
ctest -L "creation|move" |
Bu yazımla birlikte birim testlere yönelik konulara şimdilik bir nokta koyuyoruz. Bir sonraki yazımda, GitHub actions ile CI/CD kabiliyetini yazılımımıza kazandırıyor olacağız. Belki bir iki ufak, bonus konuya da değiniriz. Bu sıcak yaz vakitlerin, serin ve bol kodlu günler diliyorum 🙂
Kaynaklar
- https://coderefinery.github.io/cmake-workshop/testing/
- https://cfd.university/learn/automating-cfd-solver-and-library-compilation-using-cmake/how-to-automate-testing-with-cmake-ctest-and-gtest/
- https://cmake.org/cmake/help/book/mastering-cmake/chapter/Testing%20With%20CMake%20and%20CTest.html
- https://cmake.org/cmake/help/latest/manual/ctest.1.html