Booléens

Les deux valeurs booléennes sont : « vrais » et « faux ». Respectivement #t ou #true et #f ou #false dans Guile.

Dans un contexte de test conditionnel, « vrai » signifie toute expression autre que #f ou #false

Je t’invite à créer un nouveau répertoire dans l’espace de travail dédié à ce chapitre : ~/Workspace/guile-handbook/booleans.

Écrire le test d’abord

Création d’un fichier booleans-test.scm :

(use-modules (srfi srfi-64)
             (booleans))

(test-begin "harness")

(test-equal "true-inverted-returns-false"
  #f
  (boolean-invert #t))
  
(test-end "harness")

Essayer et lancer le test

guile -L . booleans-test.scm

Erreur de compilation !

;;; note: auto-compilation is enabled, set GUILE_AUTO_COMPILE=0
;;;       or pass the --no-auto-compile argument to disable.
;;; compiling /home/jeko/Workspace/guile-handbook/booleans/booleans-test.scm
;;; WARNING: compilation of /home/jeko/Workspace/guile-handbook/booleans/booleans-test.scm failed:
;;; no code for module (booleans)

Écrire le minimum de code pour faire compiler le test et vérifier la raison de son échec

Les erreurs de compilation peuvent être vues comme des tests rouges. Dans la tradition du TDD, il est primordial de n’ajouter que le strict nécessaire de code pour corriger ces erreurs.

Ici, le compilateur indique que le module booleans n’existe pas. Je le crée pour corriger cette erreur et le relance immédiatement le test.

Création d’un fichier booleans.scm :

(define-module (booleans))

Une nouvelle erreur survient ! Cette fois-ci, ça compile, mais je suis averti que la variable boolean-invert n’est pas liée (autrement dit, elle n’est pas définie). Je l’ajoute à mon module précédemment créé et je ré-exécute le test.

Modification du fichier booleans.scm :

(define-module (booleans))

(define-public (boolean-invert bool)
  0)

Plus d’erreur ou d’avertissement à la compilation. Le test échoue et tu peux vérifier la raison de l’échec dans le rapport harness.log. Un rapide coup d’oeil me confirme que la raison de l’échec est que le test true-inverted-returns-false attend la valeur #f alors que la procédure boolean-invert retourne 0.

$ cat harness.log 
%%%% Starting test harness
Group begin: harness
Test begin:
  test-name: "true-inverted-returns-false"
  source-file: "booleans-test.scm"
  source-line: 6
  source-form: (test-equal "true-inverted-returns-false" #f (boolean-invert #t))
Test end:
  result-kind: fail
  actual-value: 0
  expected-value: #f
Group end: harness
# of unexpected failures  1

Faire passer le test

Je modifie la procédure boolean-invert afin qu’elle retourne la valeur #f comme attendue par le test.

Modification du fichier booleans.scm :

(define-module (booleans))

(define-public (boolean-invert bool)
  #f)

L'exécution du test affiche le message suivant :

$ guile -L . booleans-test.scm
;;; note: source file /home/jeko/Workspace/guile-handbook/booleans/booleans-test.scm
;;;       newer than compiled /home/jeko/.cache/guile/ccache/3.0-LE-8-4.3/home/jeko/Workspace/guile-handbook/booleans/booleans-test.scm.go
;;; note: auto-compilation is enabled, set GUILE_AUTO_COMPILE=0
;;;       or pass the --no-auto-compile argument to disable.
;;; compiling /home/jeko/Workspace/guile-handbook/booleans/booleans-test.scm
;;; compiled /home/jeko/.cache/guile/ccache/3.0-LE-8-4.3/home/jeko/Workspace/guile-handbook/booleans/booleans-test.scm.go
%%%% Starting test harness  (Writing full log to "harness.log")
# of expected passes      1

Le test passe !

Réusinage

Il n'y a pas grand-chose à faire pour si peu de code.

Docstring

J'en profite pour te parler des docstrings. Ces chaines de caractères apportent un peu d'explication à l'utilisateur au besoin :

  • dans le REPL, avec la commande ,describe
  • dans Emacs

Ce sont des chaines de caractère placées en deuxième position dans la liste des paramètres lors de la définition d'une variable ou d'une procédure avec define, define* ou define-public (j'en passe et des meilleurs).

Ajoutons une docstring à la procédure boolean-invert !

Modification du fichier booleans.scm :

(define-module (booleans))

(define-public (boolean-invert bool)
  "Returns the opposite value of the given boolean."
  #f)

Écrire le test d’abord

Le premier test vérifiait que appeler la procédure boolean-invert avec le paramètre #t retourne #f. Le prochain test vérifiera l’inverse.

Modification du fichier booleans-test.scm :

(use-modules (srfi srfi-64)
             (booleans))

(test-begin "harness")

(test-equal "true-inverted-returns-false"
  #f
  (boolean-invert #t))
 
(test-equal "false-inverted-returns-true"
  #t
  (boolean-invert #f))
  
(test-end "harness")

Essayer et lancer le test

$ guile -L . booleans-test.scm 

Ça compile sans problème !

Écrire le minimum de code pour faire compiler le test et vérifier la raison de son échec

Le test ne passe pas.

booleans-test.scm:10: FAIL false-inverted-returns-true

Je vérifie que la raison de l’échec est celle que j’attends. C’est-à-dire que le test false-inverted-returns-true attend la valeur #t mais obtient #f.

Test begin:
  test-name: "false-inverted-returns-true"
  source-file: "booleans-test.scm"
  source-line: 10
  source-form: (test-equal "false-inverted-returns-true" #t (boolean-invert #f))
Test end:
  result-kind: fail
  actual-value: #f
  expected-value: #t

C'est confirmé.

Faire passer le test

Ajoutons le minimum de code suffisant à faire passer le test :

(define-module (booleans))

(define-public (boolean-invert bool)
  "Returns the opposite value of the given boolean."
  (if bool
      #f
      #t))

Je réexécute les tests et…

%%%% Starting test harness  (Writing full log to "harness.log")
# of expected passes      2

Tout est OK !

Réusinage

Comme tu t’en doutais, il existe un procédure toute prête pour inverser un booléen :

(define-module (booleans))

(define-public (boolean-invert bool)
  "Returns the opposite value of the given boolean."
  (not bool))

Je relance une dernière fois les tests pour vérifier que ce réusinage n’a rien cassé :

%%%% Starting test harness  (Writing full log to "harness.log")
# of expected passes      2

Tout est bon !

Conclusion

Ce que l’on a couvert dans ce chapitre :

  • Encore plus de pratique du TDD
  • Quelques notions sur les booléens
  • Écrire du code auto-documenté grâce aux docstrings