Skip to content

Commit

Permalink
Define common tests via define_tests! macro
Browse files Browse the repository at this point in the history
Signed-off-by: Joe Richey <[email protected]>
  • Loading branch information
josephlr committed Jun 20, 2024
1 parent 788d6d5 commit 71a2ab8
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 44 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -258,8 +258,8 @@ jobs:
- name: Test (Safari)
if: runner.os == 'macOS'
run: wasm-pack test --headless --safari --features=js,test-in-browser
- name: Test (custom getrandom)
run: wasm-pack test --node --features=custom
- name: Test (custom getrandom, no unit tests)
run: wasm-pack test --node --features=custom --test=custom
- name: Test (JS overrides custom)
run: wasm-pack test --node --features=custom,js

Expand Down
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -404,3 +404,6 @@ pub fn getrandom_uninit(dest: &mut [MaybeUninit<u8>]) -> Result<&mut [u8], Error
// since it returned `Ok`.
Ok(unsafe { slice_assume_init_mut(dest) })
}

#[cfg(test)]
mod tests;
123 changes: 81 additions & 42 deletions src/tests.rs
Original file line number Diff line number Diff line change
@@ -1,19 +1,12 @@
use super::getrandom_impl;
use crate::Error;

#[cfg(all(target_arch = "wasm32", target_os = "unknown"))]
use wasm_bindgen_test::wasm_bindgen_test as test;
extern crate std;
use std::{mem::MaybeUninit, sync::mpsc::channel, thread, vec, vec::Vec};

#[cfg(feature = "test-in-browser")]
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);

#[test]
fn test_zero() {
// Test that APIs are happy with zero-length requests
getrandom_impl(&mut [0u8; 0]).unwrap();
}

// Return the number of bits in which s1 and s2 differ
#[cfg(not(feature = "custom"))]
fn num_diff_bits(s1: &[u8], s2: &[u8]) -> usize {
assert_eq!(s1.len(), s2.len());
s1.iter()
Expand All @@ -23,14 +16,9 @@ fn num_diff_bits(s1: &[u8], s2: &[u8]) -> usize {
}

// Tests the quality of calling getrandom on two large buffers
#[test]
#[cfg(not(feature = "custom"))]
fn test_diff() {
let mut v1 = [0u8; 1000];
getrandom_impl(&mut v1).unwrap();

let mut v2 = [0u8; 1000];
getrandom_impl(&mut v2).unwrap();
pub(crate) fn check_diff_large(make_vec: fn(usize) -> Vec<u8>) {
let v1 = make_vec(1000);
let v2 = make_vec(1000);

// Between 3.5 and 4.5 bits per byte should differ. Probability of failure:
// ~ 2^(-94) = 2 * CDF[BinomialDistribution[8000, 0.5], 3500]
Expand All @@ -40,21 +28,16 @@ fn test_diff() {
}

// Tests the quality of calling getrandom repeatedly on small buffers
#[test]
#[cfg(not(feature = "custom"))]
fn test_small() {
pub(crate) fn check_small(make_vec: fn(usize) -> Vec<u8>) {
// For each buffer size, get at least 256 bytes and check that between
// 3 and 5 bits per byte differ. Probability of failure:
// ~ 2^(-91) = 64 * 2 * CDF[BinomialDistribution[8*256, 0.5], 3*256]
for size in 1..=64 {
let mut num_bytes = 0;
let mut diff_bits = 0;
while num_bytes < 256 {
let mut s1 = vec![0u8; size];
getrandom_impl(&mut s1).unwrap();
let mut s2 = vec![0u8; size];
getrandom_impl(&mut s2).unwrap();

let s1 = make_vec(size);
let s2 = make_vec(size);
num_bytes += size;
diff_bits += num_diff_bits(&s1, &s2);
}
Expand All @@ -63,19 +46,7 @@ fn test_small() {
}
}

#[test]
fn test_huge() {
let mut huge = [0u8; 100_000];
getrandom_impl(&mut huge).unwrap();
}

// On WASM, the thread API always fails/panics
#[cfg(not(target_arch = "wasm32"))]
#[test]
fn test_multithreading() {
extern crate std;
use std::{sync::mpsc::channel, thread, vec};

pub(crate) fn check_multithreading(make_vec: fn(usize) -> Vec<u8>) {
let mut txs = vec![];
for _ in 0..20 {
let (tx, rx) = channel();
Expand All @@ -84,10 +55,8 @@ fn test_multithreading() {
thread::spawn(move || {
// wait until all the tasks are ready to go.
rx.recv().unwrap();
let mut v = [0u8; 1000];

for _ in 0..100 {
getrandom_impl(&mut v).unwrap();
make_vec(1000);
thread::yield_now();
}
});
Expand All @@ -98,3 +67,73 @@ fn test_multithreading() {
tx.send(()).unwrap();
}
}

// Helper trait for testing different kinds of functions.
// DUMMY generic parameter is needed to avoid conflicting implementations.
pub(crate) trait FillFn<const DUMMY: usize> {
fn make_vec(self, len: usize) -> Vec<u8>;
}
impl<F: Fn(&mut [u8]) -> Result<(), Error>> FillFn<0> for F {
fn make_vec(self, len: usize) -> Vec<u8> {
let mut v = vec![0; len];
self(&mut v).unwrap();
v
}
}
impl<F: Fn(&mut [MaybeUninit<u8>]) -> Result<(), Error>> FillFn<1> for F {
fn make_vec(self, len: usize) -> Vec<u8> {
let mut v = Vec::with_capacity(len);
self(v.spare_capacity_mut()).unwrap();
unsafe { v.set_len(len) };
v
}
}
impl<F: Fn(&mut [MaybeUninit<u8>]) -> Result<&mut [u8], Error>> FillFn<2> for F {
fn make_vec(self, len: usize) -> Vec<u8> {
let mut v = Vec::with_capacity(len);
let ret = self(v.spare_capacity_mut()).unwrap();
assert_eq!(ret.len(), len);
assert_eq!(ret.as_ptr(), v.as_ptr());
unsafe { v.set_len(len) };
v
}
}

macro_rules! define_tests {
($fill:path) => {
use crate::tests::FillFn;
#[cfg(all(target_family = "wasm", target_os = "unknown"))]
use wasm_bindgen_test::wasm_bindgen_test as test;

#[test]
fn zero() {
$fill.make_vec(0);
}
#[test]
fn diff_large() {
crate::tests::check_diff_large(|len| $fill.make_vec(len));
}
#[test]
fn small() {
crate::tests::check_small(|len| $fill.make_vec(len));
}
#[test]
fn huge() {
$fill.make_vec(100_000);
}
// On WASM, the thread API always fails/panics.
#[test]
#[cfg_attr(target_family = "wasm", ignore)]
fn multithreading() {
crate::tests::check_multithreading(|len| $fill.make_vec(len));
}
};
}
pub(crate) use define_tests;

mod init {
super::define_tests!(crate::getrandom);
}
mod uninit {
super::define_tests!(crate::getrandom_uninit);
}

0 comments on commit 71a2ab8

Please sign in to comment.