diff --git a/src/en/01_introduction.md b/src/en/01_introduction.md index 026d61e..170533c 100644 --- a/src/en/01_introduction.md +++ b/src/en/01_introduction.md @@ -69,7 +69,7 @@ Rust ecosystem for secure development. A following chapter focuses on precautions to take when choosing and using external libraries. Then, recommendations about the Rust language constructs are exposed. - +TODO: Finally, we introduce advices for writing +tests for a project in Rust. A summary of recommendations presented throughout the document is listed at the end of this guide. diff --git a/src/en/02_devenv.md b/src/en/02_devenv.md index 1e1d4cb..b1aaa54 100644 --- a/src/en/02_devenv.md +++ b/src/en/02_devenv.md @@ -250,3 +250,77 @@ There exist other useful tools or `cargo` subcommands for enforcing program security whether by searching for specific code patterns or by providing convenient commands for testing or fuzzing. They are discussed in the following chapters, according to their goals. + +## Using an internal registry + +The default Rust registry is [crates.io](https://crates.io). However, it is now possible to set up an internal registry for your organisation. + +This allows you to limit the libraries and versions available within an organisation. + +> ### Rule {{#check DENV-REGISTRY | Use of internal registry}} +> +> It is recommended that an internal registry is created if the organisation wishes or needs to have full control over the technical stack that may be used for projects. + +### Deploying a server + +There are two ways of deploying an internal registry. +One way is to develop your own solution. This must meet the API requirements [described here](https://doc.rust-lang.org/cargo/reference/registry-web-api.html). + +Second, it is possible to use a solution that has already been built. A relatively exhaustive list, validated by the rust project, can be found on the [cargo project wiki pages](https://github.com/rust-lang/cargo/wiki/Third-party-registries +) + +> ### Rule {{#check DENV-REGISTRY-LC | Internal registry management processes}} +> +> If an internal register is deployed, a lifecycle management process needs to be put in place. +> +> This should contain procedures for : +> - requesting the addition of a crate +> - validating of a crate on +> - a security point of view +> - ensuring that the tool/lib is consistent with the company's requirements +> - updating the various crates +> - removing/deleting crates + +### Using multiple registers + +It is possible to configure `cargo` to retrieve data from multiple registries. This is done by adding the registers to the `.cargo/config.toml` file. + +```toml +[registries] + = { index = "https://:/git/index" } +``` + +Each new registry must be added by a new line in the configuration file. The name of the registry is defined by the value of \. + +> ### Rule {{#check DENV-REGISTRY-CONF | Authorised registry configuration}} +> +> All registries authorised within an organisation must be approved and reviewed on a regular basis by the various competent bodies. +> +>The naming of registries must be consistent throughout the organisation in order to guarantee the operation of projects, regardless of the developer. + +### Setting up a default registry + +To globally configure the use of a default registry, you need to modify the `.cargo/config.toml` file to add the lines below. + +```toml +[registry] +default = "" +``` + +> ### Rule {{#check DENV-REGISTRY-DEFAULT | Setting up a default registry}} +> +> In the event that an internal registry is deployed, it is advisable to set the organisation's internal registry as the default Rust registry. + +### Publishing a project + +If a user wishes to publish their project internally only or on several private registries, then they need to add the following lines to their `Cargo.toml` file. + + +```toml +[package] +publish = [""] +``` + +> ### Rule {{#check DENV-REGISTRY-PUBLISH | Publication in internal registry}} +> +> If a project or library is developed in an internal context and is not intended to be made public, then it is necessary to ensure that the package is published in the internal registry only. \ No newline at end of file diff --git a/src/en/03_libraries.md b/src/en/03_libraries.md index 51a0750..49dc915 100644 --- a/src/en/03_libraries.md +++ b/src/en/03_libraries.md @@ -38,16 +38,53 @@ reported to the RustSec Advisory Database. [cargo-audit]: https://github.com/RustSec/cargo-audit - - - - - +## Checking the supply chain + +Through its security working group, Rust offers a number of tools for checking the security of a program's supply-chain at library level. + +### Cargo supply-chain + +[Cargo-supply-chain] is the tool developed by the rust foundation's official working group, which collects all the people who can work on the libraries used by the project. + +> ### Rule {{#check LIBS-SUPPLY-CHAIN | Check developers implicitly trusted}} +> +> The `cargo-supply-chain` tool must be used to find out who your organisation implicitly trust to run your project. + +[cargo-supply-chain]: https://github.com/rust-secure-code/cargo-supply-chain +### Cargo vet / crev + +[Cargo-vet] is a tool developed by the Mozilla Foundation that allows you to check whether the libraries you can use have been audited by trusted third parties. + +> Rule {{#check LIBS-VET | Priority use of libraries that have been audited}} +> +> It is advisable to use the `cargo-vet` tool to prioritise the use of libraries which have been audited by third parties. + +Security audits can be created using a tool called [Cargo-crev]. The use of this tool will not be detailed in this guide. + +For more information, please consult the tool's [official documentation]. + +> ### Advises +> +> We recommend that you carry out security audits using the `cargo-crev` tool in order to check the security of the +> of the libraries used in your project and to share them with the community. + +[cargo-vet]: https://github.com/mozilla/cargo-vet +[cargo-crev]: https://github.com/crev-dev/cargo-crev +[official documentation]: https://github.com/crev-dev/cargo-crev/blob/main/cargo-crev/src/doc/getting_started.md + +## Unsafe code in libraries + +[Cargo-geiger] is a tool maintained by the Rust security working group. +Its aim is to detect the use of the `unsafe` block in a project's supply chain. + +The results have three levels: +1) 🔒 = No `unsafe` usage found, declares #![forbid(unsafe_code)] +2) ❓ = No `unsafe` usage found, missing #![forbid(unsafe_code)] +3) ☢️ = `unsafe` usage found + +> ### Rule {{#check LIBS-UNSAFE | Check *unsafe* code in dependencies}} +> +> Use the `cargo-geiger` tool to check that uses of the `unsafe` block comply with the recommendations described in the following section of this guide. + + +[cargo-geiger]: https://github.com/geiger-rs/cargo-geiger \ No newline at end of file diff --git a/src/en/04_language.md b/src/en/04_language.md index 8491118..c937544 100644 --- a/src/en/04_language.md +++ b/src/en/04_language.md @@ -66,6 +66,19 @@ manipulations of memory pointers, the language provides the `unsafe` keyword. > the crate root (typically `main.rs` or `lib.rs`) to generate compilation > errors if `unsafe` is used in the code base. + > ### Information +> +>You can also obtain the same result by adding one of the two blocks below to the `Cargo.toml` file. + +```toml +[lints.rust] +unsafe_code="forbid" +``` + +```toml +[lints.clippy] +unsafe_code = "forbid" +``` ## Integer overflows Although some verification is performed by Rust regarding potential integer @@ -112,25 +125,57 @@ else { println!("{}", res); } > specialized functions `overflowing_`, `wrapping_`, or the > `Wrapping` type must be used. - - ## Error handling - - The `Result` type is the preferred way of handling functions that can fail. A `Result` object must be tested, and never ignored. +> ### Recommendation {{#check LANG-ERRDO | Use the `?` operator and do not use the `try!` macro}} +> +> The `?` operator should be used to improve readability of code. +> The `try!` macro should not be used. + +### Custom Error type implementation + > ### Recommendation {{#check LANG-ERRWRAP | Implement custom `Error` type, wrapping all possible errors}} > > A crate can implement its own `Error` type, wrapping all possible errors. > It must be careful to make this type exception-safe (RFC 1236), and implement > `Error + Send + Sync + 'static` as well as `Display`. + +To ensure that the above recommendation is implemented correctly, you can use the following code: +```rust +pub enum Error { + ... // Implement Error enum here +} -> ### Recommendation {{#check LANG-ERRDO | Use the `?` operator and do not use the `try!` macro}} +#[cfg(test)] +mod test { + fn rfc1236(){} + + #[test] + fn test_rfc1236(){ + rfc1236::(); + } + +} +``` +> ### Recommendation {{#check LANG-ERR-FLAT | root positioning of type `Error` }} > -> The `?` operator should be used to improve readability of code. -> The `try!` macro should not be used. +> It is advisable to publicly position this type at the root of your API. For example: `crate::Error`. + +To do this, you can flatten the `Result` and `Error` types using the following piece of code placed at the root of the `src/lib.rs` or `src/main.rs` file: + +```rust +pub use error::Error; +``` + +The advantage of this technique is that, in your code, you can make the position of the type in your project agnostic for the user or for yourself. + +When using external libraries, you can either wrap the type or use a library such as [derive_more]. + +[derive_more]: https://crates.io/crates/derive_more +### Third-party library use Third-party crates may be used to facilitate error handling. Most of them (notably [failure], [snafu], [thiserror]) address the creation of new custom diff --git a/src/en/08_testfuzz.md b/src/en/08_testfuzz.md index 0f20fce..c978294 100644 --- a/src/en/08_testfuzz.md +++ b/src/en/08_testfuzz.md @@ -2,10 +2,146 @@ ## Writing tests -TODO: good practices in writing tests. +Rust offers two types of test built in by default: internal tests and integration tests. +In this section, we will discuss these two types of test as well as a rather special type of test, which is the trait implementation test. -## Fuzzing +> Recommendation {{#check TEST-DRIVEN-DEV | Adopt a test-driven development's method}} +> +> One of the best development habits is to start development by writing the set of tests to which the functionality must respond. + +### Internal + +Internal tests define all the tests present in the `src/` folder of a Rust project. They have the great advantage of being able to test all the functions (even private ones) if they are placed in the same file as the project. + + +> ### Recommendation {{#check TEST-INTERNE | Test all functions}} +> +> It is advisable to test all the functions of your programme, even those that may seem the most trivial. +> +> This way, if a future modification causes a side-effect that modifies the behaviour of another function, you will notice it much more quickly. +> This also helps to limit the number of bugs as early as possible. + +```rust +// private function +fn my_function(){ + ... // Your code here +} + +#[cfg(test)] +mod tests{ + #[test] + fn test_my_function(){ + ... // Your tests here + } +} +``` + +It is also possible to ignore certain tests if they take too long to run. This can be done by adding `#[ignore]` above the test function. + +```rust +#[cfg(test)] +mod tests{ + #[test] + fn test_non_ignore(){ ... } + + #[ignore] + #[test] + fn test_ignore() { ... } +} +``` + +> Important +> +> If a test is marked as ‘ignore’, it will no longer be possible to run it even if you specify its name using the `cargo test ` command. +> +> The only way it can be run is to remove the `#[ignore]` above it. + +> ### Recommendation {{#check TEST-IGNORE | Limit the number of ignored tests}} +> +> It is recommended to limit the number of tests that will be ignored as much as possible. + +Rust has an attribute system that allows part of the code to be compiled only when necessary. +This makes it possible to define code that will only be compiled when a particular feature is requested. + +One of the basic features of any project is `test`. This allows you to describe code which will only be present when the code is compiled for testing (via the `cargo test` command). + +To do this, add the `#[cfg(test)]` attribute to the line above the function or module concerned: +```rust +#[cfg(test)] +mod test{ + + #[test] + fn test_1(){} +} +``` + +> ### Rules {{#check TEST-CFG | Wrap tests in a sub-module with the attribute `#[cfg(test)]`}} +> +> All internal tests must be wrapped in a sub-module with the `#[cfg(test)]` attribute. +> +> Similarly, any potential functions you may develop to help these tests must also have the `#[cfg(test)]` attribute. +### Integration + +> Attention +> +> This type of test is only available for crates which are libraries. + +The integration tests are the set of tests in the `tests/` folder at the root of the crate. +In this folder, each `*.rs` file will be compiled as a different crate and the library tested will be used as if an external project were using it. + +For example, if we were developing a library called `example`, we could run the following integration test: +```rust +use example::method_name; + +#[test] +fn test_api(){ + method_name(); +} +``` + +These tests are run at the same time as all the other tests using the following command: +```bash +cargo test +``` + +> ### Rule {{#check TEST-IMPL | Check that the public behaviour of the API is as expected}} +> +> Integration tests must ensure that the library's behaviour is as expected. These tests must cover all the solution's public functions (including the import of types, functions, enums, etc.). +> +> This also ensures that the API is user-friendly. + +### Implementing a trait + +The example below is used to create a test to ensure that a struct or enum implements a trait. + +These tests are a little unusual. If positioned in a project, they can prevent the project from compiling if they are not valid. + +Here is an example of code used to ensure that an enum has the Send and Sync traits: + +```rust +enum Example {} + +#[cfg(test)] +mod test{ + use super::*; + + fn send_sync_trait(){} + + #[test] + fn test_traits_impl(){ + send_sync_trait::(); + } +} +``` + +> ### Recommendation {{#check TEST-TRAIT | Create tests to ensure that certain traits are implemented for structures/enums}} +> +> In certain contexts, it is necessary for certain struct or enum to implement particular traits. +> In such cases, it is strongly recommended that you implement this type of test. + + \ No newline at end of file diff --git a/src/en/SUMMARY.md b/src/en/SUMMARY.md index 26adcd2..7bfcf40 100644 --- a/src/en/SUMMARY.md +++ b/src/en/SUMMARY.md @@ -7,7 +7,6 @@ - [Memory management](05_memory.md) - [Type system](06_typesystem.md) - [Foreign Function Interface](07_ffi.md) +- [Test and fuzzing](08_testfuzz.md) -[Licence](LICENCE.md) - - +[Licence](LICENCE.md) \ No newline at end of file diff --git a/src/fr/01_introduction.md b/src/fr/01_introduction.md index b4fbf2d..48a5bcd 100644 --- a/src/fr/01_introduction.md +++ b/src/fr/01_introduction.md @@ -75,6 +75,6 @@ nous proposons des recommandations concernant l'utilisation des outils de l'écosystème Rust dans un cadre sécurisé. Ensuite, nous détaillons les précautions à prendre durant le choix et l'utilisation de bibliothèques externes. Ensuite, les recommandations à propos des constructions du langage -sont présentées. Un résumé des règles et +sont présentées. Enfin, nous discutons de la bonne utilisation des outils de +test pour un projet réalisé en Rust. Un résumé des règles et recommandations est disponible à la fin de ce guide. diff --git a/src/fr/02_devenv.md b/src/fr/02_devenv.md index 0c0690b..e5fe0ba 100644 --- a/src/fr/02_devenv.md +++ b/src/fr/02_devenv.md @@ -69,8 +69,6 @@ $ rustup override list $ ``` - - > ### Règle {{#check DENV-STABLE | Utilisation de la chaîne d'outils *stable*}} > > Le développement d'applications sécurisées doit être mené en utilisant la @@ -285,3 +283,76 @@ D'autres outils ou sous-commandes `cargo` utiles pour renforcer la sécurité d'un programme existent, par exemple, en recherchant des motifs de code particuliers. Nous en discutons dans les chapitres suivants en fonction de leurs portées et de leurs objectifs. + +## Utilisation d'un registre interne + +Le registre Rust par défaut est [crates.io](https://crates.io). Cependant, il est maintenant possible de mettre en place pour son organisme un registre interne. + +Celui-ci permet de limiter les librairies et les versions disponibles au sein de ladite organisation. + +> ### Règle {{#check DENV-REGISTRY | Utilisation d'un registres interne}} +> +> Il est conseillé de mettre en place un registre interne dans le cas où l'organisation souhaiterait ou aurait la nécessité de maîtriser entièrement la stack techniques pouvant être utilisées par les projets. + +### Déploiment d'un serveur + +Pour pouvoir utiliser un registre interne, il existe deux façons de faire. +Tout d'abord, il est possible de développer sa propre solution. Celle-ci doit répondre aux exigences d'API [décrite ici](https://doc.rust-lang.org/cargo/reference/registry-web-api.html). + +Enfin, il est possible d'utiliser une solution déjà construite. Une liste relativement exhaustive et validée par le projet rust est disponible dans les [pages du wiki du projet cargo](https://github.com/rust-lang/cargo/wiki/Third-party-registries +) + +> ### Règle {{#check DENV-REGISTRY-LC | Processus de gestions du registre interne}} +> +> Dans le cas où un registre interne serait déployé, il est nécessaire de mettre en place un processus de gestion de son cycle de vie. +> +> Celui-ci devra contenir les procédures permettant : +> - de demande d'ajout d'une crate +> - de validation d'une crate +> - d'un point sécurité +> - de la cohérence de l'outil/lib dans les souhaits de l'entreprise +> - de mise à jour des différentes crates +> - de retrait/suppression des crates + +### Utilisation de multiples registres + +Il est possible de configurer `cargo` afin que celui-ci puisse récupérer des données depuis plusieurs registres. Pour ce faire, il faut ajouter les registres dans le fichier `.cargo/config.toml`. + +```toml +[registries] + = { index = "https://:/git/index" } +``` + +Chaque nouveau registre devra être ajouté par une nouvelle ligne dans le fichier de configuration. Le nom du registre qui est défini par la valeur de \ + +> ### Règle {{#check DENV-REGISTRY-CONF | Configuration de registre autorisée}} +> +> L'ensemble des registres autorisées dans une organisation doivent être approuvée et revue de manière régulière par les différentes entités compétentes. +> +>Le nommage des registres devra être cohérent au sein de l'organisation afin de garantir le fonctionnement des projets quel que soit le développeur. + +### Mise en place d'un registre par défaut + +Pour configurer de manière globale l'utilisation d'un registre par défaut, il faut modifier le fichier `.cargo/config.toml` pour y ajouter les lignes ci-dessous. + +```toml +[registry] +default = "" +``` + +> ### Règle {{#check DENV-REGISTRY-DEFAULT | Mise en place d'un registry par défaut}} +> +> Dans le cas où un registre interne serait déployé, il est conseillé de mettre le registre interne à l'organisation en tant que registre Rust par défaut. + +### Publication d'une projet + +Dans le cas où un utilisateur souhaiterait publier son projet en interne uniquement ou sur plusieurs registres privées alors il lui faut ajouter les lignes suivantes dans son fichier `Cargo.toml`. + +```toml +[package] +publish = [""] +``` + +> ### Règle {{#check DENV-REGISTRY-PUBLISH | Publication dans le registre interne}} +> +> Si un projet ou une librairie est développé dans un contexte interne et n'as pas vocation à être rendu publique alors il est nécessaire de s'assurer que le package sera bien publié dans le registre interne uniquement. \ No newline at end of file diff --git a/src/fr/03_libraries.md b/src/fr/03_libraries.md index 841d8a6..0c3938d 100644 --- a/src/fr/03_libraries.md +++ b/src/fr/03_libraries.md @@ -42,18 +42,55 @@ un projet. > connues dans les dépendances d'un projet. [cargo-audit]: https://github.com/RustSec/cargo-audit +## Vérification de la supply-chain - +Rust propose, via son groupe de travail sur la sécurité un certain nombre d'outils permettant de s'assurer de la sécurité de la supply-chain d'un programme au niveau de ses bibliothèques. - +### Cargo supply-chain - +> Il est conseillés de faire d'audits de sécurités via l'outil `cargo-crev` afin de vérifier la sécurité +> des librairies utilisées dans votre projet et d'en faire profiter la communauté. + +[cargo-vet]: https://github.com/mozilla/cargo-vet +[cargo-crev]: https://github.com/crev-dev/cargo-crev +[documentation officielle]: https://github.com/crev-dev/cargo-crev/blob/main/cargo-crev/src/doc/getting_started.md +## Code *unsafe* dans les bibliothèques + +[Cargo-geiger] est un outil maintenu par le groupe de travail permettant de sécuriser Rust. +Son but est de détecter l'utilisation du block `unsafe` dans la supply-chain d'un projet. + +Les résultats possèdent trois niveaux : +1) 🔒 = pas d'utilisation du bloc `unsafe` trouvée, la ligne #![forbid(unsafe_code)] est déclarés +2) ❓ = pas d'utilisation du bloc `unsafe` trouvée, la ligne n'est pas #![forbid(unsafe_code)] est déclarés +3) ☢️ = utilisation de bloc `unsafe` trouvée dans le code + +> ### Règle {{#check LIBS-UNSAFE | Vérification du code *unsafe* dans les dépendances}} +> +> Utiliser l'outil `cargo-geiger` afin de vérifier que les usages du block `unsafe` respectent bien les recommandations décrites dans la section suivantes de ce guide. + +> Attention +> +> To date, the `cargo-geiger` tool does not take into account when #![forbid(unsafe_code)] is in its second form in the `Cargo.toml` file. + +[cargo-geiger]: https://github.com/geiger-rs/cargo-geiger \ No newline at end of file diff --git a/src/fr/04_language.md b/src/fr/04_language.md index 27b52a1..8878275 100644 --- a/src/fr/04_language.md +++ b/src/fr/04_language.md @@ -73,6 +73,19 @@ langage fournit le mot-clé `unsafe`. > afin de générer des erreurs de compilation dans le cas ou le mot-clé `unsafe` > est utilisé dans le projet. +> ### Information +> +>Il est également possible d'obtenir le même résultat en rajoutant l'un des deux blocs ci-dessous au fichier `Cargo.toml`. + +```toml +[lints.rust] +unsafe_code="forbid" +``` + +```toml +[lints.clippy] +unsafe_code = "forbid" +``` ## Dépassement d'entiers Bien que des vérifications soient effectuées par Rust en ce qui concerne les @@ -124,14 +137,17 @@ else { println!("{}", res); } ## Gestion des erreurs - - Le type `Result` est la façon privilégiée en Rust pour décrire le type de retour des fonctions dont le traitement peut échouer. Un objet `Result` doit être testé et jamais ignoré. +> ### Recommandation {{#check LANG-ERRDO | Utilisation de l'opérateur `?` et non-utilisation de la macro `try!`}} +> +> L'opérateur `?` doit être utilisé pour améliorer la lisibilité du code. +> La macro `try!` ne doit pas être utilisée. + +### Implémentation d'un type d'Erreur personnalisé + > ### Recommandation {{#check LANG-ERRWRAP | Mise en place d'un type `Error` personnalisé, pouvant contenir toutes les erreurs possibles}} > > Une *crate* peut implanter son propre type `Error` qui peut contenir toutes @@ -139,10 +155,39 @@ testé et jamais ignoré. > ce type doit être *exception-safe* (RFC 1236) et implémenter les traits > `Error + Send + Sync + 'static` ainsi que `Display`. -> ### Recommandation {{#check LANG-ERRDO | Utilisation de l'opérateur `?` et non-utilisation de la macro `try!`}} +Pour s'assurer que la recommandation ci-dessus soit bien implémenter, vous pouvez utiliser le code suivant : +```rust +pub enum Error { + ... // Implement Error enum here +} + +#[cfg(test)] +mod test { + fn rfc1236(){} + + #[test] + fn test_rfc1236(){ + rfc1236::(); + } + +} +``` + +> ### Recommandation {{#check LANG-ERR-FLAT | positionnement à la racine du type `Error` }} > -> L'opérateur `?` doit être utilisé pour améliorer la lisibilité du code. -> La macro `try!` ne doit pas être utilisée. +> Il est conseillé de positionner publiquement ce type à la racine de votre API. Par exemple : `crate::Error`. + +Pour ce faire, vous pouvez aplatir les types `Result` et `Error` via le morceau de code suivant placé à la racine du fichier `src/lib.rs` ou `src/main.rs` : +```rust +pub use error::Error; +``` + +L'avantage de cette technique, est qu'elle permet, dans votre code, de rendre agnostique pour l'utilisateur ou pour vous-même, la position du type dans votre projet. + +Lorsque vous utilisez des bibliothèques externes, vous pouvez soit wrapper le type soit utiliser une bibliothèque comme [derive_more]. + +[derive_more]: https://crates.io/crates/derive_more +### Utilisation de bibliothèque tierce Des *crates* tierces peuvent être utilisées pour faciliter la gestion d'erreurs. La plupart ([failure], [snafu], [thiserror]) proposent la création de types diff --git a/src/fr/08_testfuzz.md b/src/fr/08_testfuzz.md index 1bf1ac6..9def65f 100644 --- a/src/fr/08_testfuzz.md +++ b/src/fr/08_testfuzz.md @@ -2,11 +2,144 @@ ## Test -TODO : bonnes pratiques pour écrire des tests. +Rust propose deux types de test intégrés par défaut : les tests internes ainsi que les tests d'intégration. +Dans cette section, nous discuterons de ces deux types de tests ainsi que d'un type de test un peu particulier qui est le test d'implémentation de trait. -## Fuzzing +> ### Recommandation {{#check TEST-DRIVEN-DEV | Adoptez la méthode de développement par les tests}} +> +> Une des bonnes habitudes de développement est de commencer à développer en écrivant l'ensemble des tests auquel doit répondre la fonctionnalité. +### Interne + +Les tests dits internes définissent l'ensemble des tests étant présent dans le dossier `src/` d'un projet Rust. Ceux-ci présentent le grand avantage de pouvoir tester l'ensemble des fonctions (même privées) si placé dans le même fichier que celui-ci. + +> ### Recommandation {{#check TEST-INTERNE | Tester aux maximum les différentes fonctionnalités}} +> +> Il est conseillé l'ensemble des fonctions de votre programme même celle qui peuvent sembler le plus trivial. +> +> Ainsi, dans le cas où une modification future provoquerait un effet de bord modifiant le comportement d'une autre fonction, vous le remarquerez bien plus rapidement. +> Cela permet également de limiter le nombre de bugs au plus tôt. + +```rust +// fonction privée +fn my_function(){ + ... // Votre code ici +} + +#[cfg(test)] +mod tests{ + #[test] + fn test_my_function(){ + ... // Vos tests ici + } +} +``` + +Il est également possible d'ignorer certains tests dans le cas où ceux-ci prendraient trop de temps à être effectué. Cela peut être fait en rajouter `#[ignore]` au-dessus de la fonction de test. + +```rust +#[cfg(test)] +mod tests{ + #[test] + fn test_non_ignore(){ ... } + + #[ignore] + #[test] + fn test_ignore() { ... } +} +``` + +> ### Important +> +> Dans le cas où un test est marqué comme étant a ignorer, il ne sera plus possible de l’exécuter même si vous précisez son nom en utilisant la commande `cargo test `. +> +> Le seul moyen qu'il puisse être lancé et de lui enlever le `#[ignore]` au-dessus de lui. + +> ### Recommandation {{#check TEST-IGNORE | Limiter au maximum le nombre de tests ignoré}} +> +> Il est recommandé de limiter au maximum le nombre de tests qui seront ignorée + +Rust possède un système d'attribue permettant de ne compiler une partie du code que si c'est nécessaire. +Cela permet notamment de définir du code qui ne sera compilé que lorsqu'une feature particulière est demandé. + +L'une des caractéristiques présente de base dans tout projet est `test`. Celle-ci permet de décrire du code qui ne sera présent que lorsque le code est compilé pour être testé (via la commande `cargo test`). + +Pour ce faire, il faut ajouter l'attribue `#[cfg(test)]` sur la ligne au dessus de la fonction ou du module concerné : +```rust +#[cfg(test)] +mod test{ + + #[test] + fn test_1(){} +} +``` + +> ### Règles {{#check TEST-CFG | Encadrer les tests dans un sous-module ayant l'attribue `#[cfg(test)]`}} +> +> L'ensemble des tests internes doivent être englobé dans un sous-module ayant l'attribue `#[cfg(test)]`. +> +> De même, les potentielles fonctions que vous pouvant être développé pour aider ces tests doivent avoir également avoir l'attribue `#[cfg(test)]`. + +### Intégration + +> ### Attention +> +> Ce type de test n'est disponible que pour les crates qui sont des librairies. + +Les tests d'intégrations sont l'ensemble de tests présents dans le dossier `tests/` à la racine de la crate. +Dans ce dossier, chaque fichier `*.rs` sera compilé comme étant une crate différentes et la bibliothèque testée sera à utiliser comme si un projet externe l'utilisait. + +Par exemple, dans si nous développions une librairie nommé `exemple`, nous pourrions faire le test d'intégration suivant : +```rust +use exemple::method_name; + +#[test] +fn test_api(){ + method_name(); +} +``` + +Ces tests sont lancés en même temps que l'ensemble des autres tests via la commande suivante : +```bash +cargo test +``` + +> ### Règle {{#check TEST-IMPL | Vérifier que le comportement publique de l'API est bien celui attendus}} +> +> Les tests d'intégration doivent permettre de s'assurer que le comportement de la librairie est celui attendu. Ces tests doivent couvrir l'ensemble des fonctionnalités publiques de la solution (y compris l'import de type, fonction, enum, etc.). +> +> Cela permets également de s'assurer de la bonne ergonomie de l'API. +### Implémentation de trait + +L'exemple ci-dessous permet de créer un test permettant de s'assurer qu'une struct ou qu'une enum implémente bien un trait. + +Ces tests sont un peu particuliers. En effet, si positionné dans un projet, alors ils peuvent empêcher le projet de se compiler s'il ne sont pas valide. + +Voici un exemple de code permettant de s'assurer qu'une enum possède bien les Traits Send et Sync : +```rust +enum Exemple {} + +#[cfg(test)] +mod test{ + use super::*; + + fn send_sync_trait(){} + + #[test] + fn test_traits_impl(){ + send_sync_trait::(); + } +} +``` + +> ### Recommandation {{#check TEST-TRAIT | Créer des tests permettant de s'assurer que certains traits sont bien implémenter pour des structures/enums}} +> +> Dans certains contextes, il est nécessaires que certaines struct ou enum implémente certains traits particuliers. +> Dans ce genre de cas, il est vivement conseillé d'implémenter ce genre de test. + + \ No newline at end of file diff --git a/src/fr/SUMMARY.md b/src/fr/SUMMARY.md index 6d25a56..fbf5c7a 100644 --- a/src/fr/SUMMARY.md +++ b/src/fr/SUMMARY.md @@ -7,7 +7,7 @@ - [Gestion de la mémoire](05_memory.md) - [Système de types](06_typesystem.md) - [FFI](07_ffi.md) +- [Test et fuzzing](08_testfuzz.md) [License](LICENSE.md) -