Comment adapter les tests de mon unité à cmake et ctest?

Jusqu'à présent, j'ai utilisé une procédure improvisée de test d'unité - essentiellement une charge entière de programmes de test d'unité exécutés automatiquement par un fichier batch. Bien que beaucoup de ceux - ci vérifient explicitement leurs résultats, beaucoup plus tricher-ils déchargent les résultats dans les fichiers texte qui sont suivis en versions. Tout changement dans les résultats des tests est signalé par subversion et je peux facilement identifier ce qu'était le changement. De nombreux tests de sortie dot fichiers ou une autre forme qui me permet d'obtenir une représentation visuelle de l' sortie.

le problème est que je passe à l'utilisation de cmake. Aller avec le flux cmake signifie utiliser des compilations hors source, ce qui signifie que la commodité du dumping résulte en un dossier source/build partagé et les versionner avec la source ne fonctionne pas vraiment.

comme remplacement, ce que je faire est-à-dire l'unité de l'outil de test où trouver les fichiers de résultats attendus (dans l'arborescence des sources) et de lui faire faire la comparaison. En cas d'échec, il doit fournir les résultats réels et la liste des différences.

est-ce possible, ou devrais-je adopter une approche complètement différente?

Évidemment, Je ignorez ctest et adaptez ce que j'ai toujours fait à des constructions hors-source. Je pourrais modifier mon dossier-where-all-the-builds-live, par exemple (avec l'utilisation libérale de 'ignore' bien sûr). C'est que sain d'esprit? Probablement pas, car chaque Compilation se terminerait par une copie distincte des résultats attendus.

Aussi, tout conseils sur la façon recommandée de faire des tests unitaires avec cmake/ctest gratuitement reçu. J'ai perdu pas mal de temps avec cmake, pas parce que c'est mauvais, mais parce que je ne comprenais pas comment travailler avec ça.

EDIT

à la fin, j'ai décidé de garder le côté cake/ctest de l'Unité de test aussi simple que possible. Pour tester réels contre résultats prévus, j'ai trouvé une maison pour la fonction suivante dans ma bibliothèque...

bool Check_Results (std::ostream              &p_Stream  ,
                    const char                *p_Title   ,
                    const char               **p_Expected,
                    const std::ostringstream  &p_Actual   )
{
  std::ostringstream l_Expected_Stream;

  while (*p_Expected != 0)
  {
    l_Expected_Stream << (*p_Expected) << std::endl;
    p_Expected++;
  }

  std::string l_Expected (l_Expected_Stream.str ());
  std::string l_Actual   (p_Actual.str ());

  bool l_Pass = (l_Actual == l_Expected);

  p_Stream << "Test: " << p_Title << " : ";

  if (l_Pass)
  {
    p_Stream << "Pass" << std::endl;
  }
  else
  {
    p_Stream << "*** FAIL ***" << std::endl;
    p_Stream << "===============================================================================" << std::endl;
    p_Stream << "Expected Results For: " << p_Title << std::endl;
    p_Stream << "-------------------------------------------------------------------------------" << std::endl;
    p_Stream << l_Expected;
    p_Stream << "===============================================================================" << std::endl;
    p_Stream << "Actual Results For: " << p_Title << std::endl;
    p_Stream << "-------------------------------------------------------------------------------" << std::endl;
    p_Stream << l_Actual;
    p_Stream << "===============================================================================" << std::endl;
  }

  return l_Pass;
}

Une unité typique test ressemble maintenant...

bool Test0001 ()
{
  std::ostringstream l_Actual;

  const char* l_Expected [] =
  {
    "Some",
    "Expected",
    "Results",
    0
  };

  l_Actual << "Some" << std::endl
           << "Actual" << std::endl
           << "Results" << std::endl;

  return Check_Results (std::cout, "0001 - not a sane test", l_Expected, l_Actual);
}

là où j'ai besoin d'une fonction de vidage de données réutilisable, il faut un paramètre de type std::ostream&, de sorte qu'il peut se déverser dans un flux de résultats réels.

20
demandé sur Steve314 2010-07-22 07:14:08

1 réponses

J'utiliserais le mode de script autonome de CMake pour exécuter les tests et comparer les sorties. Normalement pour un programme de test unitaire, vous écririez add_test(testname testexecutable), mais vous pouvez exécuter n'importe quelle commande comme test.

Si vous écrivez un script "runtest.cmake " et exécutez votre programme de test de l'unité via ceci, puis le test.le script cmake peut faire tout ce qu'il veut - y compris utiliser le cmake -E compare_files utilitaire. Vous voulez quelque chose comme ce qui suit dans vos listes de Cmakel.fichier txt:

enable_testing()
add_executable(testprog main.c)
add_test(NAME runtestprog
    COMMAND ${CMAKE_COMMAND}
    -DTEST_PROG=$<TARGET_FILE:testprog>
    -DSOURCEDIR=${CMAKE_CURRENT_SOURCE_DIR}
    -P ${CMAKE_CURRENT_SOURCE_DIR}/runtest.cmake)

Cela exécute script (cmake-P runtest.cmake) et définit 2 variables: TEST_PROG, défini au chemin de l'exécutable de test, et SOURCEDIR, défini au répertoire source courant. Vous avez besoin du premier pour savoir quel programme exécuter, le second pour savoir où trouver les fichiers des résultats de test attendus. Le contenu de runtest.cmake:

execute_process(COMMAND ${TEST_PROG}
                RESULT_VARIABLE HAD_ERROR)
if(HAD_ERROR)
    message(FATAL_ERROR "Test failed")
endif()

execute_process(COMMAND ${CMAKE_COMMAND} -E compare_files
    output.txt ${SOURCEDIR}/expected.txt
    RESULT_VARIABLE DIFFERENT)
if(DIFFERENT)
    message(FATAL_ERROR "Test failed - files differ")
endif()

Le premier execute_process exécute le programme de test, qui écrira "sortie.txt". Si cela fonctionne, alors la prochaine execute_process s'exécute efficacement cmake -E compare_files output.txt expected.txt. Fichier "devrait.txt " est le bon résultat connu dans votre arborescence des sources. Si il y a des différences, c'erreurs de sorte que vous pouvez voir le test échoué.

ce que cela ne fait pas, c'est d'imprimer les différences; CMake n'a pas d'implémentation "diff" complète cachée à l'intérieur. Au moment où vous utilisez Subversion pour voir quelles lignes ont changé, une solution évidente est donc de changer la dernière partie en:

if(DIFFERENT)
    configure_file(output.txt ${SOURCEDIR}/expected.txt COPYONLY)
    execute_process(COMMAND svn diff ${SOURCEDIR}/expected.txt)
    message(FATAL_ERROR "Test failed - files differ")
endif()

ceci écrase l'arborescence des sources avec la sortie de compilation sur la défaillance puis exécute svn diff. Le problème est que vous ne devriez pas vraiment changer l'arbre des sources de cette façon. Quand vous faites le test une deuxième fois, il passe! Une meilleure solution est d'installer visual diff outil et l'exécuter sur votre sortie et prévu de fichier.

18
répondu richq 2010-07-22 12:43:41