Skip to content

Point U : tests qui doivent échouer à compiler, test_input, sorties du manuel en CI - #97

Open
Jordan08 wants to merge 9 commits into
configure-cleanfrom
todo-u-small-errors
Open

Jordan08 wants to merge 9 commits into
configure-cleanfrom
todo-u-small-errors

Conversation

@Jordan08

@Jordan08 Jordan08 commented Oct 6, 2026 •

Copy link
Copy Markdown
Owner

Point U de TODO.md : les petites erreurs qui restaient (anciens 40 et 64).

40. Un test qui doit échouer à compiler restait en échec une fois son objet compilé

Problème. Les tests nodiscard_discard_cxx11, _cxx14 et _cxx17, comme refused_finite_math_only et refused_fast_math, construisent avec cmake --build un objet dont la compilation doit échouer, et passent si la sortie du build contient le message du compilateur. Si la compilation réussit une fois (un en-tête en régression, ou la vérification qu'un test échoue sans sa correction), l'objet existe ; si l'en-tête est ensuite rétabli avec son ancienne date de modification (cp -p, une archive), l'objet est à jour, le build ne compile rien, n'écrit aucun message, et le test reste en échec jusqu'à ce qu'on supprime l'objet à la main. Reproduit sur configure-clean : GAOL_NODISCARD vidé dans gaol/gaol_config.h, puis le fichier rétabli par cp -p : les trois nodiscard_discard_* restent en échec (« Required regular expression not found »). Même chose pour les deux refused_* en retirant les deux #error de -ffast-math et -ffinite-math-only.

Correction. tests/CMakeLists.txt a une fonction gaol_compile_test(), que prennent gaol_refused_test() et gaol_nodiscard_test() (ce qu'elles répétaient chacune : cible OBJECT hors du build, répertoires d'inclusion et définitions de gaol::gaol, verrou gaol_compile_tests). Pour un test dont la compilation doit échouer, la cible compile une copie de la source dans l'arbre de build (configure_file(... COPYONLY)), et le test lance tests/compile_error.cmake, qui refait cette copie depuis la source, la touche (file(TOUCH)), puis lance le build : la copie, plus récente que l'objet, est toujours recompilée, quelles que soient les dates des en-têtes et de la source. La copie est refaite parce que CMake ne reconfigure pas pour une source rétablie avec son ancienne date (cas trouvé par la relecture). Les tests qui doivent compiler (nodiscard_used_*, refused_positive) ne changent pas. Les cinq tests sont corrigés : la question de refused_*, qui ont le même défaut, a été posée, et tranchée pour les deux.

Alternatives écartées : supprimer l'objet avant le build, par son chemin ($<TARGET_OBJECTS> dans add_test(), qui fonctionne avec CMake 3.14 et le générateur Makefile) : le chemin des objets sous les générateurs Visual Studio, à plusieurs configurations, n'est pas vérifiable ici, alors qu'une source plus récente que son objet est recompilée par tous les générateurs ; OBJECT_DEPENDS sur un fichier toujours plus récent : la documentation de CMake dit que les générateurs Visual Studio et Xcode ne le prennent pas en compte.

Vérifié : avec les sources de configure-clean, le scénario ci-dessus laisse les tests en échec ; avec la branche, la mutation les fait échouer (« Required regular expression not found »), puis, l'en-tête rétabli par cp -p, ils passent (les trois nodiscard_discard_* et les deux refused_* qui doivent échouer, refused_positive restant réussi). De même quand c'est la source du test (tests/nodiscard.cpp, tests/refused_options.cpp) qu'on mute puis rétablit par cp -p. Le relecteur a vérifié le mécanisme avec CMake 4.4 et 3.14 (Makefile), Ninja, et Ninja Multi-Config ($<CONFIG> arrive au script, l'objet est dans .dir/Debug/).

40. test_input() avalait une exception et sautait six vérifications

Problème. Dans tests/input_output.cpp, un try hérité du check/ de GAOL 4 entourait les vérifications de la lecture et écrivait l'exception sur cerr. "<-inf,-inf>" lève input_format_error (« bounds of degenerate interval do not evaluate to the same value », comme le manuel le dit : -inf se lit [-oo, -MAX], qui n'est pas un double) : la vérification de GAOL 4, qui attendait [-oo, -MAX], et les six suivantes ([-inf, -inf], [-inf], inf, [inf], [inf,inf], <inf,inf>) ne s'exécutaient jamais, et le test passait.

Correction. Plus de try autour des vérifications : une exception inattendue est un échec, compté par unit_tests.h. <-inf,-inf> et <inf,inf> doivent lever input_format_error dont l'explication parle de « degenerate », sous #if GAOL_EXCEPTIONS_ENABLED comme le cas <3, 4> voisin. test_input passe de 15 à 22 vérifications, toutes réussies, et le test n'écrit plus rien sur la sortie d'erreur.

Vérifié : une mutation du parser ([a] avec une borne infinie non vide, une ligne de gaol_interval_parser.cpp) passe l'ancien test (« 15 checks, 0 failed ») et fait échouer le nouveau (2 échecs, [-inf] et [inf]). Une autre (le refus des bornes dégénérées retiré) fait échouer les deux par <3, 4>, et le nouveau aussi par <-inf,-inf> et <inf,inf>.

40. Le programme qui compare les sorties du manuel au programme

Problème. run_examples.py, qui avait montré le 30 septembre que 30 exemples du manuel n'imprimaient pas ce que le manuel montre (#55), était resté dans le bloc-notes des agents et est perdu. Décidé le 3 octobre : il va dans manual/. Décidé pour ce point, le 6 octobre : il tourne aussi dans la CI.

Correction. manual/check_examples.py, réécrit d'après la description du rapport de #55 : il extrait les 88 exemples de manual/v5/gaol.tex qui montrent une sortie (@outputs^...~), compile chacun avec un GAOL installé ($CXX, et les drapeaux de pkg-config gaol), l'exécute, et compare ce qu'il imprime au manuel, ligne à ligne, espaces compris. Un exemple qui a un main() est compilé tel quel ; les autres sont mis dans un main(), après <gaol/gaol>, <gaol/gaol_expression.h> et les en-têtes standard qu'ils emploient, avec using namespace std et using namespace gaol, ou gaol_ieee1788 dans le chapitre de ses noms. @rem^...~ donne le commentaire, @textasciitilde un tilde, @version la version installée ; une ligne imprimée trop longue pour la page peut être montrée sur plusieurs lignes de sortie (un seul cas, l'exemple de atan), chaque coupure valant un blanc. Une erreur de compilation renvoie à la ligne de l'exemple dans gaol.tex (#line). Chaque exemple est un programme à part : un format réglé par l'un n'atteint pas le suivant. Options : -j, --std, --keep, -v. Le statut de sortie est 1 si un exemple ne compile pas, échoue ou imprime autre chose, et aussi si une ligne @outputs^ du manuel n'est pas dans un exemple que le script lit, ou s'il n'en trouve aucun (sans quoi un exemple écrit autrement, \begin{example}% par exemple, aurait été sauté sans le dire). Ce que le manuel montre autrement, dans un bloc onscreen après un programme ou dans un commentaire « Prints ... », n'est pas vérifié, ce que l'en-tête du script dit.

linux.yml le lance dans le job « Ubuntu 24.04 x86_64 GCC », après l'installation, en C++11 (le standard le plus ancien où compilent les en-têtes de GAOL), pour qu'un changement de la bibliothèque qui change une sortie du manuel se voie ; manual.yml ne tourne que quand manual/ change, et ne construit pas GAOL.

Vérifié : 88 sur 88 avec les GAOL installés par les builds SSE2, FPU et Clang 18, en C++11 et C++17 (et avec GCC 9.4 en C++14 et C++2a) ; le manuel d'avant #55 (df4cd92^1) avec la bibliothèque d'aujourd'hui : 36 écarts, dont les 28 exemples qui impriment 1 et 0 au lieu de true et false, et les sorties changées depuis (la notation [a] des points, le format agreeing) ; une sortie altérée dans une copie du manuel : 1 écart, statut 1.

40. Une sortie du manuel que le script ne voit pas

La relecture a relevé dans le manuel (l. 1469, un bloc onscreen sans programme) interval(0.1) = [0x1.999999999999ap-4, 0x1.999999999999ap-4], alors que GAOL v5 écrit en hexadécimal un intervalle ponctuel [0x1.999999999999ap-4] depuis #71 (vérifié avec le GAOL installé ; la ligne one_tenth est juste) : corrigé. Les deux autres blocs onscreen qui suivent un programme complet (l. 835 et 4161) impriment bien ce que le manuel montre (programmes lancés à la main).

40. Le commentaire du format hexadécimal

Le commentaire de display_bounds() (gaol/gaol_interval.cpp) disait que le format hexadécimal écrit les signes des bornes d'un intervalle ponctuel nul, faux depuis #71 : exact_string() écrit [0x0p+0] quels que soient les signes, comme le format décimal écrit [0] (vérifié sur interval::zero(), [-0, +0], [+0, +0] et [-0, -0]). Les deux formats gardent le signe d'une borne nulle d'un intervalle non ponctuel ([-0, 5], [-0x0p+0, 0x1.4p+2]).

40 (suite). Les tests sans exceptions, et deux jobs (décidé le 6 octobre)

Problème. Aucun job ne construisait GAOL sans exceptions (--disable-exceptions de configure, -Denable-exception=false de meson), où une erreur de GAOL appelle gaol_error() puis std::abort() et où les classes d'exception n'existent pas (gaol/gaol_exceptions.h est sous #if GAOL_EXCEPTIONS_ENABLED). Dans ce mode, sur configure-clean, six programmes de test ne compilaient pas (constructor, expressions, float_functions, ieee1788, misc, numbers : ils nomment input_format_error, invalid_action_error ou gaol_exception), donc make check ne lançait rien, et input_output s'arrêtait sur <-inf,-inf> (statut 134, vérifié en le compilant seul contre ce GAOL). Les 24 autres passaient.

Correction. Comme le faisait déjà input_output, les vérifications d'une exception, et celles d'un texte que le lecteur refuse (qui arrêterait le programme), sont sous #if GAOL_EXCEPTIONS_ENABLED ; le reste de chaque test tourne dans les deux modes. refused() d'expressions ne vérifie rien sans exceptions, en le disant ; dans ieee1788, les six noms de GAOL seuls, que gaol_ieee1788::textToInterval rend vides dans les deux modes, restent vérifiés, et seuls les trois appels faux (pown([2,5],2.5), sin(1,2), fma(1,2)), qui arrêtent le programme sans exceptions, sont gardés ; les macros TEST_INOUT_* de constructor lisent sans try ; dans misc, les static_assert des noms des exceptions sont gardés. build-systems.yml gagne deux jobs, « Autotools, Ubuntu 24.04 x86_64, without exceptions » et « Meson, Ubuntu 24.04 x86_64, without exceptions », qui construisent, installent, lancent les tests et les reconstruisent avec le GAOL installé (tests.sh). Comme les autres jobs de ce workflow, ils ne construisent pas les exemples ; l'exemple 15 montre justement input_format_error et ne compile pas sans exceptions (question ouverte).

Vérifié : sans exceptions, autotools : 29 réussis, 2 sautés (intervalf, interval2f) ; ieee1788 : 6 257 vérifications sans exceptions, 6 260 avec, comme avant ; meson 0.53.2 : 29 réussis, 2 sautés ; tests.sh avec le GAOL installé : ses sept programmes réussis ; les sept fichiers de test modifiés compilent sans avertissement sous -Wall -Wextra -Werror avec GCC 9.4 et Clang 18 dans ce mode. Avec exceptions, rien ne change : CMake avec -Wall -Wextra -Werror, GCC 9.4 (tests et exemples) 57 sur 57, Clang 18 (tests) 41 sur 41.

40. Le reste du point

  • GAOL_NODISCARD sous Visual C++ 2017 15.8 et 15.9 : toujours non vérifiable. Compiler Explorer n'a, en Visual C++ 2017, que 19.10 et 19.14 (liste relue le 6 octobre).
  • chi([-oo, +oo]) reste 1, comme dans GAOL 4 et le manuel (décidé le 4 octobre) : rien à changer.

64. La mise en page de doc/tests.md

Les numéros de ligne du TODO avaient bougé : il y avait 14 lignes de plus de 80 colonnes (jusqu'à 152) et des lignes courtes au milieu d'un paragraphe (« The reading of », « With flush-to-zero, denormals-are-zero or both set in MXCSR », « NaN_val, and », « empty set; »...). Le premier commit ne change que les coupures de ligne : aucune ligne de plus de 80 colonnes, aucune ligne de moins de 62 colonnes au milieu d'un paragraphe là où le mot suivant y tenait, et le moins de lignes changées possible, les liens, le code, « GAOL v5 », « Table 10.5 », « point V », « IEEE 1788-2015 » et « ±(2^53 + 1) » n'étant jamais coupés ; le paragraphe que #96 vient de compléter (« misc declares a namespace detail of its own, which », 56 colonnes) est recoupé de même. git diff --word-diff dc8405defc20940d5ce7da888c61e4b87b68994b^ dc8405defc20940d5ce7da888c61e4b87b68994b ne montre aucun mot changé (le relecteur a aussi comparé le HTML de pandoc, identique, sur la version d'avant #96). Les paragraphes que les commits suivants complètent sont recoupés de même, sans ligne courte.

Pour doc/differences.md et ChangeLog (pull request de synthèse)

ChangeLog :

	* tests/CMakeLists.txt (gaol_compile_test): the compile tests whose
	compilation has to fail (refused_finite_math_only, refused_fast_math,
	nodiscard_discard_cxx11/14/17) compile a copy of their source, which
	tests/compile_error.cmake makes again and touches before the build:
	they stayed failed once their object was compiled and a header or
	their source restored with its former modification time.
	* tests/compile_error.cmake: new.
	* tests/input_output.cpp (test_input): <-inf,-inf> and <inf,inf>
	have to throw input_format_error; no try around the checks, which
	skipped the six after <-inf,-inf>.
	* manual/check_examples.py: new; the examples of the manual that show
	an output, compiled with an installed GAOL and run, have to print it.
	* .github/workflows/linux.yml: runs it in the job Ubuntu 24.04 x86_64
	GCC, in C++11.
	* gaol/gaol_interval.cpp (display_bounds): the comment on the
	hexadecimal format.
	* manual/v5/gaol.tex: interval(0.1) in hexadecimal, the point
	[0x1.999999999999ap-4].
	* doc/tests.md: lines of 80 columns at most.

doc/differences.md, dans la puce « The manual of GAOL v5 », après « with examples whose outputs are those of GAOL v5 » : « , which manual/check_examples.py compiles with an installed GAOL and runs, in the continuous integration too ». Rien d'autre : ces changements ne touchent pas l'utilisateur de la bibliothèque.

Relecture

Un relecteur indépendant (sous-agent sans le contexte de ce travail, avec process.md, le point U et la branche) n'a rien trouvé de bloquant. Il a reconstruit la branche et la base hors de l'arbre (CMake 4.4 GCC 9.4 : 41 sur 41 ; meson : 29 réussis, 2 sautés), rejoué le scénario de l'objet resté compilé sur la base et la branche avec Makefile (CMake 4.4 et 3.14), Ninja et Ninja Multi-Config, vérifié input_output (15 puis 22 vérifications, la mutation du refus des bornes dégénérées détectée, -Wall -Wextra -Werror, fr_FR.UTF-8), le script du manuel (88 sur 88 en C++11 et C++17 avec GCC et Clang, une sortie altérée détectée), l'étape YAML, le commentaire de gaol_interval.cpp, et les commits.

Ses remarques, toutes vérifiées puis corrigées dans la branche :

  • la copie restait périmée quand c'est la source du test, et non un en-tête, qu'on rétablit par cp -p (reproduit avec Makefile et Ninja) : le script refait la copie avant de la toucher ;
  • le script du manuel sautait sans le dire un exemple écrit \begin{example}% ou \begin{example}[...], et passait sur un manuel sans exemple : il échoue maintenant si une ligne @outputs^ lui échappe ;
  • la sortie hexadécimale de interval(0.1) du manuel (section ci-dessus) ;
  • deux paragraphes de doc/tests.md complétés par les commits suivants avaient de nouveau des lignes courtes : recoupés ;
  • le sujet d'un commit disait « the six checks after them » : c'est après <-inf,-inf>.

Les deux commits du mode sans exceptions, ajoutés ensuite, ont eu leur propre relecture indépendante. Elle a vérifié que les six programmes ne compilaient pas sans exceptions sur la base, que autotools et meson donnent 29 réussis et 2 sautés dans ce mode (avec comma-locale.sh, make install, make distclean et tests.sh contre les GAOL installés par les deux builds), l'absence d'avertissement (-Wall -Wextra -Werror -Wconversion, GCC 9.4 et Clang 18, dans les deux modes), et, en comparant les sources prétraitées avec exceptions de la base et de la branche, que le mode normal ne change pas. Corrigé à sa demande : la garde de la liste d'ieee1788 couvrait aussi six noms qui marchent sans exceptions (vérifié par une sonde : ils rendent l'ensemble vide) ; une ligne courte dans doc/tests.md et une dans doc/continuous-integration.md ; l'en-tête de build-systems.yml, qui disait les exemples absents de ces seuls jobs, alors qu'aucun job de ce workflow ne les construit.

Vérifié en local

  • CMake 4.4, tests et exemples : GCC 9.4 SSE2, FPU (-DGAOL_SIMD=OFF) et Clang 18 : 57 tests sur 57 (intervalf et interval2f sautés par construction), aucun avertissement.
  • -Wall -Wextra et CMAKE_COMPILE_WARNING_AS_ERROR, avec GCC 9.4 et Clang 18 : construits, 57 sur 57.
  • CMake 3.14.4 et 3.16.3 : configurés sans avertissement, 41 tests sur 41, dont les huit tests de compilation.
  • autotools : make, make install, make check (29 réussis, 2 sautés) ; make distclean rend l'arbre identique (diff -r vide, aucun répertoire vide).
  • meson 0.53.2 : 45 réussis, 2 sautés.
  • manual/check_examples.py : 88 sur 88, comme dit plus haut.
  • Après les corrections de la relecture, sur une copie neuve : CMake SSE2, 57 sur 57 ; les scénarios de l'en-tête et de la source rétablis par cp -p ; CMake 3.14.4, 9 tests de compilation sur 9 ; le script du manuel, 88 sur 88, et ses cas d'erreur (sortie altérée, @outputs^ hors d'un exemple lu, manuel sans exemple : statut 1).
  • Le manuel : construit par manual/build-pdf.sh (136 pages), sans erreur, avec, comme sur configure-clean, un seul « Overfull » (l. 537-541) et une seule forme de police manquante.
  • La branche refaite sur configure-clean après la fusion de Point V : gaol::lexicographic_less, un ordre total pour std::set, std::sort et std::map #96 (point V) : CMake SSE2, 57 sur 57, aucun avertissement ; le script du manuel, 88 sur 88.
  • check_branch : OK.

Non vérifiable ici : les générateurs Visual Studio pour compile_error.cmake (MSBuild doit recompiler la copie touchée, plus récente que son objet : seule la CI Windows le montre, avec les nodiscard_discard_* de Visual C++ 2019 16.4 et suivants) ; Xcode n'est pas dans la CI ; macOS (AppleClang lance les refused_* et nodiscard_*) ; GAOL_NODISCARD sous Visual C++ 2017 15.8 et 15.9.

Dépendances

Aucune. #95 (point O), fusionnée pendant ce travail, est rattrapée par une fusion de configure-clean dans la branche (57e1d48) : deux conflits simples, résolus en gardant les deux textes (l'en-tête de build-systems.yml, et dans doc/tests.md les lignes 342-344, avec le texte de #95 et le découpage à 80 colonnes de la branche). Le paragraphe que #95 a ajouté à doc/tests.md avait une ligne de 85 colonnes : recoupé de même, trois lignes, sans mot changé.

Questions ouvertes

  • Les sorties que le manuel montre hors des @outputs (blocs onscreen après un programme complet, l. 835 et 4161 ; commentaires « Prints ... » de l'exemple de la l. 4076) ne sont pas vérifiées par le script ; elles sont justes aujourd'hui (vérifiées à la main, ou par le relecteur pour les commentaires). Les écrire en @outputs les ferait vérifier, mais change la présentation du manuel : à décider.
  • Sans exceptions, l'exemple 15 (examples/15_text_and_ieee1788.cpp), qui montre input_format_error, ne compile pas, et les jobs sans exceptions ne construisent pas les exemples : faut-il l'adapter à ce mode, ou dire que les exemples supposent les exceptions ?
  • Sans exceptions, gaol_ieee1788::textToInterval rend l'ensemble vide pour un nom inconnu (une erreur de syntaxe), mais arrête le programme pour un appel faux (pown([2,5],2.5), l'erreur de la bibliothèque), alors qu'IEEE 1788-2015 attend l'ensemble vide : remarqué, non changé (hors du point U).
  • manual/check_examples.py accepte qu'une ligne imprimée soit montrée sur plusieurs lignes de sortie (un seul cas aujourd'hui, atan) : un exemple qui imprimerait sur une ligne ce que le manuel montre sur deux passerait aussi.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant