Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Extra tests #331

Merged
merged 9 commits into from
Mar 26, 2018
Merged

Extra tests #331

merged 9 commits into from
Mar 26, 2018

Conversation

pitdicker
Copy link
Contributor

@pitdicker pitdicker commented Mar 25, 2018

I have been playing around with kcov to measure code coverage. I realize that good testing is much more than code coverage, but it showed some places that could use some easy better tests.

  • In the binomial distribution, test both code paths, and not only mean but also variance. Fixes Fixes for Binomial, Poisson distributions #310.
  • In the Poisson distribution, test both code paths.
  • Better tests for the char Uniform and Alphanumeric distributions.
  • Test Range::new_inclusive and Range::sample_single.
  • Basic tests for JitterRng (to catch integer overflow etc.)

Finally there was one change I was missing. In dhardy#71 (comment) you made usize support for 16-bit better.

B.t.w. our code coverage is now 92.9%. The remaining percentages are not really testable, they are the error handling in OsRng, JitterRng, EntropyRng, ReseedingRng, and in error.rs.

Copy link
Member

@dhardy dhardy left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good stuff.


let variance =
results.iter().map(|x| (x - mean) * (x - mean)).sum::<f64>()
/ (results.len() - 1) as f64;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why -1 here?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe the formula for variance is ((x-mean)^2)/(n-1).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where did you get that from? It should be over n; you are simply averaging each sample's variance. See https://en.wikipedia.org/wiki/Variance#Discrete_random_variable

Also, it is arguable that you should be using the expected mean instead of the actual mean here. That might improve the tolerance you can use in the test?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, there is a difference between taking the variance of all results, and of only a sample of them. I should know these things better...

@@ -129,17 +129,32 @@ mod test {
use distributions::Distribution;
use super::Binomial;

fn test_binomial_mean_and_variance(n: u64, p: f64) {
let binomial = Binomial::new(n, p);
let mut rng = ::test::rng(123);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using a fixed RNG is a good idea, but better to pass either the RNG or the seed into the function so not all calls use the same sequence

// We can pick from 62 characters. This is so close to a power of 2, 64,
// that we can do better than `Range`. Use a simple bitshift and
// rejection sampling. We do not use a bitmask, because for small RNGs
// the most significant bits are usually of higher quality.
loop {
let var = rng.next_u32() >> 26;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe write (32 - 6) so people don't get the wrong idea about where 26 comes from?

.map(|()| rng.sample(Alphanumeric)).take(5).collect();
assert_eq!(word.len(), 5);
.map(|()| rng.gen::<char>()).take(1000).collect();
assert!(word.len() != 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's not much point keeping this now. There's already a doc-test on Rng doing the same thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't find that doc-test to be honest.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

$ rg Alphanumeric src/lib.rs 
309:/// use rand::distributions::{Alphanumeric, Range};
318:///         .map(|()| rng.sample(Alphanumeric))
...

Copy link
Contributor Author

@pitdicker pitdicker Mar 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see the difference 😄. I was looking for a doctest for .gen::char(). I would like to keep the test for .sample(Alphanumeric), because it checks whether the characters are actually alphanumeric.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the new one you added does, but the one above only makes a String, which seems redundant.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the one in lib.rs is redundant as a test. But it is still useful as documentation, isn't it?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, the one in lib.rs is redundant with test_chars which is pointless now you added test_alphanumeric. I'm saying delete test_chars.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But test_chars is testing the Uniform distributions for chars, not the Alphanumeric one like test_alphanumeric and the doc-test?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, you changed it. I guess I should read not just rely on memory. 👍

match JitterRng::new() {
Ok(ref mut rng) => {
// false positives are possible, but extremely unlikely
assert!(rng.next_u32() | rng.next_u32() != 0);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Testing that two single next_u32 samples are each not zero (to test the "half used" code) is probably about the only useful test to do here. Don't worry about 1 failure in 2^31 test runs; CI fails far far more often than that due to infrastructure / incompatible updates.

The other != 0 tests seem pointless to me.

Copy link
Member

@dhardy dhardy Mar 26, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean assert!(rng.next_u32() != 0) twice to check both are not 0. Edit: sorry, XOR does the same thing 👍

@pitdicker
Copy link
Contributor Author

Ready to merge?

@dhardy
Copy link
Member

dhardy commented Mar 26, 2018

You didn't reply to my other comment, about the JitterRng tests.

@pitdicker
Copy link
Contributor Author

O, I removed all except the one just above your comment.

@dhardy dhardy merged commit 1980163 into rust-random:master Mar 26, 2018
pitdicker pushed a commit that referenced this pull request Apr 4, 2018
@pitdicker pitdicker deleted the extra_tests branch April 4, 2018 17:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants