Skip to content

Commit

Permalink
Add coverage workflow to measure code quality (#11)
Browse files Browse the repository at this point in the history
* Add coverage workflow to measure code quality

* Add GRCov configuration and upload coverage reports to Codecov

* add caching to codecov workflow

* check for coverage files

* modified grcov settings

* modified grcov settings

* Update output-path in grcov.yml and codecov.yml for coverage files

* Commit changes to Codecov workflow to use grcov output

* modified grcov settings

* modified grcov settings

* modified grcov settings

* modified grcov settings

* modified grcov settings

* modified grcov settings

* modified grcov settings

* modified grcov settings

* Update codecov.yml to specify async-event-emitter test package and args

* Update grcov and codecov configuration and test dependencies in lib.rs

* Update output-type and remove llvm flag

* Remove unused `Context` import and serialize value directly in emit function

---------

Co-authored-by: Spencerjibz <[email protected]>
  • Loading branch information
spencerjibz and Spencerjibz authored Nov 3, 2023
1 parent ca9cbc6 commit c359518
Show file tree
Hide file tree
Showing 4 changed files with 282 additions and 164 deletions.
5 changes: 5 additions & 0 deletions .github/grcov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
branch: true
ignore-not-existing: true

output-type: lcov
output-path: coverage
54 changes: 54 additions & 0 deletions .github/workflows/codecov.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
name: Code Coverage
on:
push:
branches:
- main
pull_request:
branches:
- main

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v1
- uses: actions-rs/toolchain@v1
with:
toolchain: nightly
override: true
components: llvm-tools-preview
- uses: actions/cache@v3
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}-coverage

- uses: actions-rs/cargo@v1

with:
command: test
args: --all-features --no-fail-fast
env:
CARGO_INCREMENTAL: "0"
RUSTFLAGS: "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests"
RUSTDOCFLAGS: "-Zprofile -Ccodegen-units=1 -Cinline-threshold=0 -Clink-dead-code -Coverflow-checks=off -Cpanic=abort -Zpanic_abort_tests"
- name: Download grcov
run: |
mkdir -p "${HOME}/.local/bin"
curl -sL https://github.com/mozilla/grcov/releases/download/v0.8.10/grcov-x86_64-unknown-linux-gnu.tar.bz2 | tar jxf - -C "${HOME}/.local/bin"
echo "$HOME/.local/bin" >> $GITHUB_PATH
- uses: actions-rs/[email protected]
id: coverage
with:
config: .github/grcov.yml

- name: List coverage files
run: cat ${{steps.coverage.outputs.report}} >> coverage.Icov
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
228 changes: 223 additions & 5 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,6 @@
License: MIT
*/

use anyhow::Context;
use serde::{Deserialize, Serialize};
use std::{collections::HashMap, sync::Arc};
use tokio::task::{self};
Expand Down Expand Up @@ -169,10 +168,7 @@ impl AsyncEventEmitter {
if let Some(listeners) = self.listeners.get_mut(event) {
let mut listeners_to_remove: Vec<usize> = Vec::new();
for (index, listener) in listeners.iter_mut().enumerate() {
let bytes: Vec<u8> = bincode::serialize(&value).context(format!(
" typeof {} can't be serialized",
std::any::type_name::<T>()
))?;
let bytes: Vec<u8> = bincode::serialize(&value)?;

let callback = Arc::clone(&listener.callback);

Expand Down Expand Up @@ -366,3 +362,225 @@ impl fmt::Debug for AsyncEventEmitter {
.finish()
}
}

#[cfg(test)]

mod async_event_emitter {
use super::AsyncEventEmitter;
use anyhow::Ok;
use futures::{lock::Mutex, FutureExt};
use lazy_static::lazy_static;
use serde::{Deserialize, Serialize};

lazy_static! {
// Export the emitter with `pub` keyword
pub static ref EVENT_EMITTER: Mutex<AsyncEventEmitter> = Mutex::new(AsyncEventEmitter::new());
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct Date {
month: String,
day: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct Time {
hour: String,
minute: String,
}
#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
struct DateTime(Date, Time);

#[tokio::test]

async fn test_async_event() -> anyhow::Result<()> {
let mut event_emitter = AsyncEventEmitter::new();

let date = Date {
month: "January".to_string(),
day: "Tuesday".to_string(),
};

event_emitter.on("LOG_DATE", |_date: Date| {
async move { /*Do something here */ }
});

event_emitter.on("LOG_DATE", |date: Date| async move {
println!(" emitted data: {:#?}", date)
});
event_emitter.emit("LOG_DATE", date).await?;
println!("{:#?}", event_emitter);
assert!(event_emitter.listeners.get("LOG_DATE").is_some());

Ok(())
}

#[tokio::test]
async fn test_emit_multiple_args() -> anyhow::Result<()> {
let mut event_emitter = AsyncEventEmitter::new();
let time = Time {
hour: "22".to_owned(),
minute: "30".to_owned(),
};
let name = "LOG_DATE".to_string();
let payload = (
Date {
month: "January".to_string(),
day: "Tuesday".to_string(),
},
name,
time,
);

let copy = payload.clone();
event_emitter.on("LOG_DATE", move |tup: (Date, String, Time)| {
assert_eq!(tup, copy);
async move {}
});

event_emitter.emit("LOG_DATE", payload).await?;

Ok(())
}

#[tokio::test]
async fn listens_once_with_multiple_emits() -> anyhow::Result<()> {
let mut event_emitter = AsyncEventEmitter::new();
let name = "LOG_DATE".to_string();
event_emitter.once("LOG_DATE", |tup: (Date, String)| async move {
println!("{:#?}", tup)
});

event_emitter
.emit(
"LOG_DATE",
(
Date {
month: "January".to_string(),
day: "Tuesday".to_string(),
},
name.clone(),
),
)
.await?;
event_emitter
.emit(
"LOG_DATE",
(
Date {
month: "January".to_string(),
day: "Tuesday".to_string(),
},
name,
),
)
.await?;

assert_eq!(event_emitter.listeners.len(), 1);
if let Some(event) = event_emitter.listeners.get("LOG_DATE") {
println!("{:?}", event)
}

Ok(())
}
#[tokio::test]
async fn remove_listeners() -> anyhow::Result<()> {
let mut event_emitter = AsyncEventEmitter::new();
let dt = DateTime(
Date {
month: "11".to_owned(),
day: String::from("03"),
},
Time {
hour: "12".to_owned(),
minute: "50".to_owned(),
},
);
let copy = dt.clone();

let _listener_id =
event_emitter.on(
"PING",
|msg: String| async move { assert_eq!(&msg, "pong") },
);

let _listener_two = event_emitter.on("DateTime", move |d: Vec<u8>| {
let copy = dt.clone();
async move {
let a = bincode::serialize(&copy).unwrap();
assert!(!a.is_empty());
assert!(!d.is_empty())
}
});

event_emitter.emit("PING", String::from("pong")).await?;
event_emitter.emit("DateTime", copy).await?;

event_emitter.remove_listener(&_listener_id);

if let Some(event_listeners) = event_emitter.listeners.get("PING") {
assert!(event_listeners.is_empty())
}

Ok(())
}

#[tokio::test]
#[should_panic]
async fn panics_on_different_values_for_same_event() {
let mut event_emitter = AsyncEventEmitter::new();

event_emitter.on("value", |_v: Vec<u8>| async move {});

event_emitter
.emit::<&'static str>("value", "string")
.await
.unwrap();
event_emitter.emit("value", 12).await.unwrap();
}
#[tokio::test]

async fn global_event_emitter() {
// We need to maintain a lock through the mutex so we can avoid data races
EVENT_EMITTER
.lock()
.await
.on("Hello", |v: String| async move { assert_eq!(&v, "world") });
let _ = EVENT_EMITTER.lock().await.emit("Hello", "world").await;
}

// tests/listener_tests.rs

use crate::AsyncListener;
use std::sync::Arc;

#[test]
fn test_async_listener() {
// Test cases for AsyncListener struct

// Basic test with default values
let listener = AsyncListener {
callback: Arc::new(|_| async {}.boxed()),
limit: None,
id: "1".to_string(),
};

assert_eq!(listener.limit, None);
assert_eq!(listener.id.clone(), "1");

// Test with custom values
let callback = Arc::new(|_| async {}.boxed());
let limit = Some(10);
let id = "my-id".to_string();

let listener = AsyncListener {
callback,
limit,
id: id.clone(),
};

assert_eq!(listener.limit, limit);
assert_eq!(listener.id, id);

// Add more test cases to cover edge cases
}
}
Loading

0 comments on commit c359518

Please sign in to comment.