- KFC,
Ce sujet est à l'origine issu de PDP, et je n'avais pas de solution au problème. Il s'agit ici plus de recueillir des avis afin d'améliorer cette solution, ou de trouver d'autres approches.
Dans beaucoup de cas, j'utilise des types traits pour tester, par exemple, que le type d'un template donné est un type arithmétique. Parfois le comportement à tester est plus complexe. Comme toutes ces vérifictions sont faites statiquement, le code lui-même ne va pas compiler si l'on utilise quelque chose qui n'est pas prévu. Or, dans le cas d'une API que l'on fourni à un utilisateur final, cela devrait faire parti des choses à tester.
Le problème est double:
- Je ne connais pas de bibliothèque de tests unitaires qui permette de gérer ces cas, c'est à dire que la bibliothèque permette de vérifier qu'un code ne compile pas et transforme cela en succès.
- Intégrer ce comportement dans CMake. J'utilise par dessus Boost.test CMake + CTest. Or, lorsque l'on définit ses cibles avec CMake, si les tests sont activés, les tests vont être compilés et donc ces tests qui vérifient des propriétés statiques vont échouer et le processus entier de build va échouer.
Voici la solution que j'apporte:
J'ai trouvé un moyen d'obtenir ce que je veux sans que cela soit trop « hack », trop dépendant de CMake. Cela devrait en plus être portable:
Fichier staticWrapper.cpp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | #include <iostream> #include <cstdlib> constexpr const bool Compile = true; constexpr const bool NotCompile = false; bool FATAL_STATIC_ASSERT(std::string command, bool expected) { auto res = system(command.c_str()); if(expected == Compile && res != Compile) exit(res); else if(expected == NotCompile && res == 0) // C'est moche mais avec Compile à la place de 0, le test passe... exit(1); else return 0; } int main() { FATAL_STATIC_ASSERT(EO_STATIC_TEST_COMMAND, NotCompile); return 0; } |
Modification dans le fichier tests.cmake
:
1 2 3 4 5 6 7 8 9 10 11 | set(STATIC_UNIT_TEST_LIST ut-staticAssert ) foreach(test ${STATIC_UNIT_TEST_LIST}) add_executable(${test} ${TEST_DIR}/staticWrapper.cpp) target_link_libraries(${test} eo) set(STATIC_TEST_PARAM "${CMAKE_CXX_COMPILER} ${CMAKE_CXX_FLAGS} ${TEST_DIR}/${test}.cpp") target_compile_definitions(${test} PRIVATE -DEO_STATIC_TEST_COMMAND="${STATIC_TEST_PARAM}") add_test(${test} ${test}) endforeach() |
C'est évidemment un brouillon mais l'idée est là.
Le test en lui même, ut-staticAssert
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | #include <type_traits> template<class T, class = typename std::enable_if<std::is_arithmetic<T>::value>::type> class A {}; class B {}; int main() { A<int> a; A<B> b; return 0; } |
Le test échoue si l'on supprime la ligne A<B> b;
car cela va compiler correctement, et réussi avec la ligne car on s'attend à l'échec de la compilation. Le cahier des charges est respecté mais j'attends tout de même vos critiques ou proposition d'alternatives ou idées d'améliorations.
TODO:
- Permettre plusieurs tests dans un seul fichier
- Intégration de Boost.test (mais ça c'est plus pour mes besoins)
- Gérer différents build type (release / debug, où les flags sont différents)
- Factoriser un peu le code, qu'on puisse n'avoir qu'une liste de test, et spécifier un flag "static" par exemple.