Skip to content

Commit

Permalink
chore: fix linting warnigns and add testing workflow
Browse files Browse the repository at this point in the history
  • Loading branch information
josecelano committed Apr 10, 2024
1 parent 61b256f commit 5e0910d
Show file tree
Hide file tree
Showing 16 changed files with 285 additions and 47 deletions.
26 changes: 26 additions & 0 deletions .cargo/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
[alias]
cov = "llvm-cov"
cov-lcov = "llvm-cov --lcov --output-path=./.coverage/lcov.info"
cov-html = "llvm-cov --html"
time = "build --timings --all-targets"

[build]
rustflags = [
"-D",
"warnings",
"-D",
"future-incompatible",
"-D",
"let-underscore",
"-D",
"nonstandard-style",
"-D",
"rust-2018-compatibility",
"-D",
"rust-2018-idioms",
"-D",
"rust-2021-compatibility",
"-D",
"unused",
]

1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
@josecelano
19 changes: 19 additions & 0 deletions .github/dependabot.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
version: 2
updates:
- package-ecosystem: github-actions
directory: /
schedule:
interval: daily
target-branch: "develop"
labels:
- "Continuous Integration"
- "Dependencies"

- package-ecosystem: cargo
directory: /
schedule:
interval: daily
target-branch: "develop"
labels:
- "Build | Project System"
- "Dependencies"
126 changes: 126 additions & 0 deletions .github/workflows/testing.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
name: Testing

on:
push:
pull_request:

env:
CARGO_TERM_COLOR: always

jobs:
format:
name: Formatting
runs-on: ubuntu-latest

steps:
- id: checkout
name: Checkout Repository
uses: actions/checkout@v4

- id: setup
name: Setup Toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: nightly
components: rustfmt

- id: cache
name: Enable Workflow Cache
uses: Swatinem/rust-cache@v2

- id: format
name: Run Formatting-Checks
run: cargo fmt --check

check:
name: Static Analysis
runs-on: ubuntu-latest
needs: format

strategy:
matrix:
toolchain: [nightly, stable]

steps:
- id: checkout
name: Checkout Repository
uses: actions/checkout@v4

- id: setup
name: Setup Toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.toolchain }}
components: clippy

- id: cache
name: Enable Workflow Cache
uses: Swatinem/rust-cache@v2

- id: tools
name: Install Tools
uses: taiki-e/install-action@v2
with:
tool: cargo-machete

- id: check
name: Run Build Checks
run: cargo check --tests --benches --examples --workspace --all-targets --all-features

- id: lint
name: Run Lint Checks
run: cargo clippy --tests --benches --examples --workspace --all-targets --all-features -- -D clippy::correctness -D clippy::suspicious -D clippy::complexity -D clippy::perf -D clippy::style -D clippy::pedantic

- id: docs
name: Lint Documentation
env:
RUSTDOCFLAGS: "-D warnings"
run: cargo doc --no-deps --bins --examples --workspace --all-features

- id: clean
name: Clean Build Directory
run: cargo clean

- id: deps
name: Check Unused Dependencies
run: cargo machete


unit:
name: Units
runs-on: ubuntu-latest
needs: check

strategy:
matrix:
toolchain: [nightly, stable]

steps:
- id: checkout
name: Checkout Repository
uses: actions/checkout@v4

- id: setup
name: Setup Toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ matrix.toolchain }}
components: llvm-tools-preview

- id: cache
name: Enable Job Cache
uses: Swatinem/rust-cache@v2

- id: tools
name: Install Tools
uses: taiki-e/install-action@v2
with:
tool: cargo-llvm-cov, cargo-nextest

- id: test-docs
name: Run Documentation Tests
run: cargo test --doc

- id: test
name: Run Unit Tests
run: cargo test --tests --benches --examples --workspace --all-targets --all-features
8 changes: 8 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,9 @@
.env
**/*.rs.bk
/.coverage/
/.idea/
/.vscode/launch.json
/flamegraph.svg
/target
callgrind.out
perf.data*
7 changes: 7 additions & 0 deletions .vscode/extensions.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"recommendations": [
"streetsidesoftware.code-spell-checker",
"rust-lang.rust-analyzer",
"tamasfe.even-better-toml"
]
}
35 changes: 35 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"[rust]": {
"editor.formatOnSave": true
},
"[ignore]": { "rust-analyzer.cargo.extraEnv" : {
"RUSTFLAGS": "-Z profile -C codegen-units=1 -C inline-threshold=0 -C link-dead-code -C overflow-checks=off -C panic=abort -Z panic_abort_tests",
"RUSTDOCFLAGS": "-Z profile -C codegen-units=1 -C inline-threshold=0 -C link-dead-code -C overflow-checks=off -C panic=abort -Z panic_abort_tests",
"CARGO_INCREMENTAL": "0",
"RUST_BACKTRACE": "1"
}},
"rust-analyzer.checkOnSave": true,
"rust-analyzer.check.command": "clippy",
"rust-analyzer.check.allTargets": true,
"rust-analyzer.check.extraArgs": [
"--",
"-D",
"clippy::correctness",
"-D",
"clippy::suspicious",
"-W",
"clippy::complexity",
"-W",
"clippy::perf",
"-W",
"clippy::style",
"-W",
"clippy::pedantic"
],
"evenBetterToml.formatter.allowedBlankLines": 1,
"evenBetterToml.formatter.columnWidth": 130,
"evenBetterToml.formatter.trailingNewline": true,
"evenBetterToml.formatter.reorderKeys": true,
"evenBetterToml.formatter.reorderArrays": true,

}
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Testing in Rust

[![Testing](https://github.com/nautilus-cyberneering/testing-in-rust/actions/workflows/testing.yaml/badge.svg)](https://github.com/nautilus-cyberneering/testing-in-rust/actions/workflows/testing.yaml)

A collection of articles about testing in Rust.

- [Custom mocks in Rust](./docs/custom-mocks-in-rust.md).
Expand All @@ -8,7 +10,7 @@ A collection of articles about testing in Rust.
## Links

- ["Unit Testing" book](https://www.manning.com/books/unit-testing) by [Vladimir Khorikov](https://github.com/vkhorikov).
- [Mockall](https://github.com/asomers/mockall). A powerful mock object library for Rust().
- [Mockall](https://github.com/asomers/mockall). A powerful mock object library for Rust.
- [Rust book. "A Use Case for Interior Mutability: Mock Objects"](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#a-use-case-for-interior-mutability-mock-objects) by [Steve Klabnik](https://steveklabnik.com/) and [Carol Nichols](http://carol-nichols.com/).
- [Mock Objects (manually) in Rust](https://paytonrules.com/post/mock-objects-in-rust/) by [Eric Smith](https://github.com/paytonrules).
- [A guide to mocking in Rust using Mockall](https://blog.logrocket.com/guide-mocking-rust-mockall/) by Manish Shivanandhan.
54 changes: 27 additions & 27 deletions docs/custom-mocks-in-rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ fn the_tracker_should_send_a_connect_event_after_connecting() {
// Test using a custom mock for the TrackerEventSender

let event_sender = Rc::new(TrackerEventSenderMock::new());
let tracker = Arc::new(Tracker::new(event_sender.clone()));
let tracker = Rc::new(Tracker::new(event_sender.clone()));

tracker.connect();

Expand All @@ -88,7 +88,7 @@ self.sent_event = Some(event);

Since the `self` reference is not mutable, you can not change the `sent_event` value.

I needed to learn how to implement it, and [Cameron](https://github.com/da2ce7) pointed me to the solution. The not mutable `self` reference does not allow you to change the attributes in the struct, but it's not recursive. Rust has some types that allow you to change the interior mutability.
I needed to learn how to implement it, and [Cameron](https://github.com/da2ce7) pointed me to the solution. The immutable `self` reference does not allow you to change the attributes in the struct, but it's not recursive. Rust has some types that allow you to change the interior mutability.

Rust has a pattern called the ["Interior Mutability Pattern"](https://doc.rust-lang.org/book/ch15-05-interior-mutability.html#refcellt-and-the-interior-mutability-pattern).

Expand All @@ -111,39 +111,39 @@ From the Rust book, you can see the different types to "bend" Rust rules:
The final test was like this:

```rust
#[derive(Clone)]
struct TrackerEventSenderMock {
pub sent_event: RefCell<Option<Event>>,
}
#[derive(Clone)]
struct TrackerEventSenderMock {
pub sent_event: RefCell<Option<Event>>,
}

impl TrackerEventSenderMock {
pub fn new() -> Self {
Self {
sent_event: RefCell::new(None),
}
impl TrackerEventSenderMock {
pub fn new() -> Self {
Self {
sent_event: RefCell::new(None),
}
}
}

impl EventSender for TrackerEventSenderMock {
fn send_event(&self, event: Event) -> Result<(), Box<dyn Error>> {
*self.sent_event.borrow_mut() = Some(event);
impl EventSender for TrackerEventSenderMock {
fn send_event(&self, event: Event) -> Result<(), Box<dyn Error>> {
*self.sent_event.borrow_mut() = Some(event);

// We return the expected value
Ok(())
}
// We return the expected value
Ok(())
}
}

#[test]
fn the_tracker_should_send_a_connect_event_after_connecting() {
// Test using a custom mock for the TrackerEventSender
#[test]
fn the_tracker_should_send_a_connect_event_after_connecting() {
// Test using a custom mock for the TrackerEventSender

let event_sender = Rc::new(TrackerEventSenderMock::new());
let tracker = Arc::new(Tracker::new(event_sender.clone()));
let event_sender = Rc::new(TrackerEventSenderMock::new());
let tracker = Rc::new(Tracker::new(event_sender.clone()));

tracker.connect().unwrap();
tracker.connect().unwrap();

assert_eq!(event_sender.sent_event.borrow().unwrap(), Event::Connect);
}
assert_eq!(event_sender.sent_event.borrow().unwrap(), Event::Connect);
}
```

## Conclusion
Expand All @@ -156,9 +156,9 @@ I finally decided to use `mockall` (the mocking framework) for a few reasons:
2. The `mockall` readability is better because of its fluent style.
3. `mockall` allows you to be more precise, for example checking also the number of calls.

You can read the final solution using [mockall](https://docs.rs/mockall/latest/mockall/) [here](https://github.com/torrust/torrust-tracker/blob/develop/src/udp/handlers.rs#L426-L463):
You can read the final solution using [mockall](https://docs.rs/mockall/latest/mockall/) [here](https://github.com/torrust/torrust-tracker/blob/af52045436644ba2ba3c43195a0c1b6b0bfdbd42/src/servers/udp/handlers.rs#L490-L527):

<https://github.com/torrust/torrust-tracker/blob/develop/src/udp/handlers.rs#L426-L463>
<https://github.com/torrust/torrust-tracker/blob/af52045436644ba2ba3c43195a0c1b6b0bfdbd42/src/servers/udp/handlers.rs#L490-L527>

```rust
#[tokio::test]
Expand Down
9 changes: 5 additions & 4 deletions src/bin/custom-mocks-in-rust.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
//! ```text
//! cargo run --bin custom-mocks-in-rust

use std::{rc::Rc, sync::Arc};
//! ```
use std::rc::Rc;

use testing_in_rust::example01::{
events::TrackerEventSender, handlers::handle_connect, tracker::Tracker,
};

fn main() {
let event_sender = Rc::new(TrackerEventSender {});
let tracker = Arc::new(Tracker::new(event_sender));
let tracker = Rc::new(Tracker::new(event_sender));

handle_connect(tracker)
handle_connect(&tracker);
}
5 changes: 3 additions & 2 deletions src/bin/testing-apis-in-rust.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! ```text
//! cargo run --bin testing-apis-in-rust
//!
//! ````
use std::net::{IpAddr, Ipv4Addr, SocketAddr};

use testing_in_rust::example02::api::start_server;
Expand All @@ -8,5 +9,5 @@ use testing_in_rust::example02::api::start_server;
async fn main() {
let bind_address = SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 3030);

start_server(bind_address).await
start_server(bind_address).await;
}
5 changes: 4 additions & 1 deletion src/example01/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ pub enum Event {
}

pub trait EventSender {
/// # Errors
///
/// Will return an error if the event can't be sent.
fn send_event(&self, event: Event) -> Result<(), Box<dyn Error>>;
}

Expand All @@ -16,7 +19,7 @@ pub struct TrackerEventSender {}

impl EventSender for TrackerEventSender {
fn send_event(&self, event: Event) -> Result<(), Box<dyn Error>> {
println!("Event::{:?} sent.", event);
println!("Event::{event:?} sent.");
Ok(())
}
}
Loading

0 comments on commit 5e0910d

Please sign in to comment.