CMake & CTest: membuat tes tidak membangun tes

89

Saya mencoba CTest di CMake untuk menjalankan beberapa pengujian saya secara otomatis menggunakan make testtarget. Masalahnya adalah CMake tidak "memahami" bahwa pengujian yang ingin saya jalankan harus dibuat karena ini adalah bagian dari proyek.

Jadi saya mencari cara untuk menentukan ketergantungan ini secara eksplisit.

claf
sumber

Jawaban:

79

Hal ini bisa dibilang sebuah bug di CMake (sebelumnya dilacak disini ) bahwa ini tidak bekerja di luar kotak. Solusinya adalah dengan melakukan hal berikut:

add_test(TestName ExeName)
add_custom_target(check COMMAND ${CMAKE_CTEST_COMMAND}
                  DEPENDS ExeName)

Kemudian Anda dapat menjalankan make checkdan itu akan mengkompilasi dan menjalankan pengujian. Jika Anda memiliki beberapa tes, maka Anda harus menggunakan DEPENDS exe1 exe2 exe3 ...baris di atas.

richq
sumber
1
jadi saya rasa target "make test" akan tetap tidak digunakan karena sepertinya Anda harus memilih nama target yang berbeda dalam perintah add_custom_target?
claf
Ya. Satu-satunya perbedaan antara "make test" dan "make check" adalah yang pertama menunjukkan "Menjalankan pengujian ..." terlebih dahulu dan tidak memeriksa dependensi build apa pun.
richq
2
@rq - tapi bagaimana saya bisa melakukan ini dengan banyak proyek (ketika satu CMakeLists.txt adalah sub-proyek dari yang lain) sehingga masing-masing akan menentukan checktarget dan mereka mungkin bertabrakan
Artyom
2
@Artyom - dalam hal ini Anda mungkin lebih baik hanya menggunakan "uji semua" yang setara. Sebenarnya, inilah yang saya lakukan.
richq
4
Sebenarnya, beberapa menganggapnya sebagai fitur (bukan bug) dari cmake yang dapat Anda jalankan "make test" dan hanya menjalankan tes sebagaimana adanya tanpa melakukan re-build terlebih dahulu ...
DLRdave
55

Sebenarnya ada cara untuk menggunakan make test. Anda perlu menentukan build pengujian yang dapat dieksekusi sebagai salah satu pengujian, lalu menambahkan dependensi di antara pengujian tersebut. Itu adalah:

ADD_TEST(ctest_build_test_code
         "${CMAKE_COMMAND}" --build ${CMAKE_BINARY_DIR} --target test_code)
ADD_TEST(ctest_run_test_code test_code)
SET_TESTS_PROPERTIES(ctest_run_test_code
                     PROPERTIES DEPENDS ctest_build_test_code)
Iakov Nakhimovski
sumber
11
Ini adalah satu-satunya yang meningkatkan dan tidak memaksa Anda untuk membangun target "buat semua" hanya untuk menjalankan pengujian. Kemungkinan kerugiannya: detail kesalahan build pada binari hanya muncul di file LastTest.log yang dihasilkan dan bukan di stdout / stderr
Dave Abrahams
2
Jawaban yang bagus! Anda harus menambahkan konfigurasi ke target build. Jika tidak, tidak mungkin menjalankan pengujian dalam semua konfigurasi. add_test (NAME "$ {ARGV0} _BUILD" COMMAND "$ {CMAKE_COMMAND}" --build $ {CMAKE_BINARY_DIR} --target $ {target} "--config" "$ <CONFIG>")
Daniel
1
Hal ini menyumbat reporter pengujian dengan sejumlah pengujian palsu.
Jika Anda menggunakan CMake> = 3,7, pendekatan yang disarankan adalah menggunakan perlengkapan. Lihat jawaban saya di bawah.
John Freeman
13

Saya menggunakan varian jawaban richq. Di tingkat atas CMakeLists.txt, saya menambahkan target khusus build_and_test,, untuk membangun dan menjalankan semua pengujian:

find_package(GTest)
if (GTEST_FOUND)
    enable_testing()
    add_custom_target(build_and_test ${CMAKE_CTEST_COMMAND} -V)
    add_subdirectory(test)
endif()

Di berbagai CMakeLists.txtfile sub-proyek di bawah test/, saya menambahkan setiap pengujian yang dapat dieksekusi sebagai ketergantungan build_and_test:

include_directories(${CMAKE_SOURCE_DIR}/src/proj1)
include_directories(${GTEST_INCLUDE_DIRS})
add_executable(proj1_test proj1_test.cpp)
target_link_libraries(proj1_test ${GTEST_BOTH_LIBRARIES} pthread)
add_test(proj1_test proj1_test)
add_dependencies(build_and_test proj1_test)

Dengan pendekatan ini, saya hanya perlu make build_and_testdaripada make test(atau make all test), dan ini memiliki keuntungan hanya membangun kode pengujian (dan dependensinya). Sayang saya tidak bisa menggunakan nama target test. Dalam kasus saya, ini tidak terlalu buruk karena saya memiliki skrip tingkat atas yang melakukan debug dan rilis out-of-tree (dan dikompilasi silang) dengan memanggil cmakedan kemudian make, dan itu diterjemahkan testmenjadi build_and_test.

Jelas, hal-hal GTest tidak diperlukan. Saya kebetulan menggunakan / menyukai Google Test, dan ingin membagikan contoh lengkap cara menggunakannya dengan CMake / CTest. IMHO, pendekatan ini juga memiliki keuntungan karena memungkinkan saya untuk menggunakan ctest -V, yang menunjukkan keluaran Google Test saat pengujian berjalan:

1: Running main() from gtest_main.cc
1: [==========] Running 1 test from 1 test case.
1: [----------] Global test environment set-up.
1: [----------] 1 test from proj1
1: [ RUN      ] proj1.dummy
1: [       OK ] proj1.dummy (0 ms)
1: [----------] 1 test from proj1 (1 ms total)
1:
1: [----------] Global test environment tear-down
1: [==========] 1 test from 1 test case ran. (1 ms total)
1: [  PASSED  ] 1 test.
1/2 Test #1: proj1_test .......................   Passed    0.03 sec
Trevor Robinson
sumber
Dalam contoh ini, apakah ada cara untuk mendapatkan make test untuk melakukan apa yang dilakukan ctest -V daripada ctest? Keluaran ctest terlihat sangat tidak lengkap dan hanya mengatakan bahwa ada satu pengujian.
Rajiv
6

Jika Anda mencoba meniru make check, Anda mungkin menemukan entri wiki ini berguna:

http://www.cmake.org/Wiki/CMakeEmulateMakeCheck

Saya baru saja memeriksa apakah apa yang dikatakannya berhasil (CMake 2.8.10).

Samuel
sumber
1
Ini akan membangun semua file yang dapat dieksekusi saat dijalankan make check. Untuk tes dengan waktu kompilasi yang mendominasi, ini membuat ctest -Rtidak berguna.
usr1234567
4

Selamatkan diri Anda dari sakit kepala:

make all test

Berfungsi di luar kotak untuk saya dan akan membangun dependensi sebelum menjalankan pengujian. Mengingat betapa sederhananya ini, ini hampir membuat make testfungsionalitas asli menjadi nyaman karena memberi Anda opsi untuk menjalankan pengujian kompilasi terakhir bahkan jika kode Anda rusak.

bergalah
sumber
1
Tidak bekerja dengan CDash. Anda harus memanggil buat semua && ctest dan kemudian bangunan tersebut bukan bagian dari pengujian yang diupload. Jadi peringatan atau kesalahan build tidak terlihat.
usr1234567
2
Juga tidak berfungsi dengan baik jika Anda menginginkan build paralel, karena keduanya akan berjalan secara paralel: Anda perlu make -j4 all && make test. Dan itu juga tidak stabil menggunakan alat non-Make build.
Poolie
4

Jika Anda menggunakan CMake> = 3.7, maka pendekatan yang disarankan adalah menggunakan perlengkapan :

add_executable(test test.cpp)
add_test(test_build
  "${CMAKE_COMMAND}"
  --build "${CMAKE_BINARY_DIR}"
  --config "$<CONFIG>"
  --target test
)
set_tests_properties(test_build PROPERTIES FIXTURES_SETUP    test_fixture)
add_test(test test)
set_tests_properties(test       PROPERTIES FIXTURES_REQUIRED test_fixture)

Ini melakukan hal berikut:

  • Menambahkan testtarget yang dapat dieksekusi yang dibangun daritest.cpp
  • Menambahkan test_build"test" yang menjalankan Cmake untuk membangun targettest
  • Menandai test_buildtes sebagai tugas pengaturan perlengkapantest_fixture
  • Tambahkan testpengujian yang hanya menjalankan testexecutable
  • Tandai testujian untuk membutuhkan perlengkapan test_fixture.

Jadi, setiap kali pengujian testakan dijalankan, pengujian terlebih dahulu menjalankan pengujian test_build, yang membangun executable yang diperlukan.

John Freeman
sumber
Jika $<CONFIG>tidak disetel, --targetakan menjadi argumen untuk --config.
loshad vtapkah
Saya percaya $<CONFIG>selalu tidak kosong. Ini adalah ekspresi generator untuk nama konfigurasi: cmake.org/cmake/help/latest/manual/… Saya akan mengedit jawaban untuk membungkusnya dalam tanda kutip hanya karena tidak ada bedanya.
John Freeman
Bagaimana Anda menjalankan cmake? Saya lakukan cara ini: mkdir build; cd build; cmake ..; make. Dan sepertinya tidak ada default apa pun dan semua variabel terkait kosong, hingga CMAKE_BUILD_TYPEdisetel secara manual. (saat ini di Debian 10, tidak memeriksa platform lain)
loshad vtapkah
1

Inilah yang saya pecahkan dan gunakan:

set(${PROJECT_NAME}_TESTS a b c)

enable_testing()
add_custom_target(all_tests)
foreach(test ${${PROJECT_NAME}_TESTS})
        add_executable(${test} EXCLUDE_FROM_ALL ${test}.cc)
        add_test(NAME ${test} COMMAND $<TARGET_FILE:${test}>)
        add_dependencies(all_tests ${test})
endforeach(test)

build_command(CTEST_CUSTOM_PRE_TEST TARGET all_tests)
string(CONFIGURE \"@CTEST_CUSTOM_PRE_TEST@\" CTEST_CUSTOM_PRE_TEST_QUOTED ESCAPE_QUOTES)
file(WRITE "${CMAKE_BINARY_DIR}/CTestCustom.cmake" "set(CTEST_CUSTOM_PRE_TEST ${CTEST_CUSTOM_PRE_TEST_QUOTED})" "\n")

YMMV

Kerekan
sumber
0

Jawaban Derrick, disederhanakan dan dikomentari:

# It is impossible to make target "test" depend on "all":
# https://gitlab.kitware.com/cmake/cmake/-/issues/8774
# Set a magic variable in a magic file that tells ctest
# to invoke the generator once before running the tests:
file(WRITE "${CMAKE_BINARY_DIR}/CTestCustom.cmake"
    "set(CTEST_CUSTOM_PRE_TEST ${CMAKE_MAKE_PROGRAM})\n"
)

Ini tidak sepenuhnya benar, karena tidak menyelesaikan masalah konkurensi saat berjalan ninja all test, jika ada yang melakukannya. Sebaliknya, karena sekarang, Anda memiliki dua proses ninja.

(Ftr, saya juga membagikan solusi ini di sini .)

pengguna2394284
sumber
-3

Semua jawaban di atas sempurna. Tetapi sebenarnya CMake menggunakan CTest sebagai alat pengujiannya, jadi metode standar (menurut saya) untuk melakukan misi adalah:

enable_testing ()
add_test (TestName TestCommand)
add_test (TestName2 AnotherTestCommand)

Kemudian jalankan cmake dan buatlah untuk membangun target. Setelah itu, Anda dapat menjalankan uji make , atau hanya menjalankan

ctest

kamu akan mendapatkan hasilnya. Ini diuji di bawah CMake 2.8.

Periksa detailnya di: http://cmake.org/Wiki/CMake/Testing_With_CTest#Simple_Testing

holmescn
sumber
5
Tidak dipilih karena terkadang Anda hanya ingin membuat target yang diperlukan untuk pengujian yang benar-benar dijalankan.
Dave Abrahams
12
Jawaban ini tampaknya salah paham pertanyaan: OP sudah melakukan persis seperti jawaban ini merekomendasikan: Menggunakan ctest, enable_testing(), add_test(), dll Masalahnya adalah bahwa ia harus secara manual mengeluarkan membangun perintah sebelum menjalankan tes. Dia ingin make testtargetnya secara otomatis membuat pengujian yang dapat dieksekusi jika diperlukan.
bames53
-4

Semua jawaban bagus, tetapi menyiratkan pelanggaran tradisi untuk menjalankan tes demi perintah make test. Saya telah melakukan trik ini:

add_test(NAME <mytest>
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}
COMMAND sh -c "make <mytarget>; $<TARGET_FILE:<mytarget>>")

Ini berarti bahwa pengujian terdiri dari pembuatan (opsional) dan menjalankan target yang dapat dieksekusi.

dyomas
sumber
6
:-D Aturan # 1: Jangan gunakan sistem tanpa sh. Apakah Anda tahu sistem seperti itu?
dyomas
11
Ya, Windows adalah salah satunya.
David Faure
3
Ini juga di-hardcode makedan kehilangan fitur CMake dalam membuat skrip untuk alat build lainnya.
poolie