Tests 👷🏽‍♀️

Tests unitaires et d'intégration

Des tests sont fournis avec le squelette. Ils vérifieront que les fonctions de base du buffer circulaire fonctionnent comme on l'attend, mais également que les éléments sont bien détruits au bon moment (et seulement à ce moment là).

Lancez les tests à l'aide de cargo test (ou cargo test --release).

On pourra remarquer dans le test tests/drop.rs l'utilisation du crate serial_test. Celui-ci fournit un nouvel attribut #[serial] qui permet de sérialiser les tests qui l'utilisent. Cela est nécessaire car nous utilisons un compteur d'instances global et si les tests tournaient en parallèle nous ne pourrions pas vérifier de manière cohérente que le nombre d'instances vivantes à un moment donné correspond bien à ce que nous prévoyons.

Miri

Miri est un outil de test pour Rust qui permet de détecter les comportements indéfinis. Par exemple, il peut détecter des accès mémoire incorrectement synchronisés (encore expérimental, mais fonctionnel).

Pour pouvoir utiliser Miri, il faut utiliser la version "nightly" de Rust (la version du jour) et ajouter le composant miri :

$ rustup toolchain add nightly
$ rustup +nightly component add miri

On peut ensuite lancer le test qui nous intéresse (test_concurrency dans concurrency.rs dans le cas présent) sous le contrôle de Miri, en utilisant la version "nightly" de Rust :

$ cargo +nightly miri test --test concurrency

Si une erreur est détectée, Miri en indiquera la cause :

#![allow(unused)]
fn main() {
test test_concurrency ... error: Undefined Behavior: Data race detected between
(1) non-atomic read on thread `test_concurrenc` and
(2) non-atomic write on thread `unnamed-2` at alloc128439.

help: (2) just happened here
148 |                 self.buffer.as_ptr().add(i).write(e);
    |
help: and (1) occurred earlier here
182 |             mem::drop(unsafe { self.buffer.as_ptr().add(i).read() });
}

Ici par exemple, il s'agit d'un accès mal synchronisé car certaines variables ont été manipulées sans le bon mode (par exemple Acquire ou Release).

On ne veut pas exécuter tous les tests sous Miri, car certains tests utilisent par exemple le crate serial_test qui fait des allocations mémoire qui seraient détectées par Miri comme des fuites mémoire et qui sont en dehors de notre contrôle. Par contre, le test test_concurrency, dans concurrency.rs, nous permet de tester les caractéristiques qui nous intéressent.

⚠️ L'exécution sous Miri est beaucoup plus lente que l'exécution normale, qui est non supervisée. Assurez vous que vos tests ne comportent pas de boucles trop longues (par exemple pour faire tourner un test 10 millions de fois).

💭 Miri est un outil très puissant pour détecter les bugs dûs à des comportements mal spécifiés, n'hésitez pas à l'utiliser dès que vous écrivez des programmes gérant manuellement des ressources !