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 9a6e51d
Show file tree
Hide file tree
Showing 17 changed files with 287 additions and 49 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
4 changes: 2 additions & 2 deletions docs/testing-apis-in-rust.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ impl ApiServerStarter {
}
```

That was the first version, and we changed but before explaining the latest version, we want to explain why we needed to change it.
That was the first version, and we changed it but before explaining the latest version, we want to explain why we needed to change it.

## ConnectionRefused error

Expand Down Expand Up @@ -103,7 +103,7 @@ pub async fn start(&mut self, addr: SocketAddr) {
}
```

That worked was we were not happy with adding a random sleep time. Because that:
That worked but we were not happy with adding a random sleep time. Because that:

- Make tests slower.
- Could make the test fail if, for some reason, the web server takes more than 100 milliseconds to be ready.
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;
}
Loading

0 comments on commit 9a6e51d

Please sign in to comment.