Skip to content

Tests/integration tests - #331

Draft
bglid wants to merge 20 commits into
mainfrom
tests/integration-tests
Draft

bglid wants to merge 20 commits into
mainfrom
tests/integration-tests

Conversation

@bglid

@bglid bglid commented Oct 4, 2026

Copy link
Copy Markdown
Owner

Description

Creates integration tests for adaptif. See discussion comment below.

Changes:

  • Adds CI job for pre-processing and caching MS dataset. Called by other CI jobs
  • updates .gitignore
  • Moves python unit tests under python/
  • Adds tests/setup_integration_data.py for setting up noisy speech dataset for integration tests
  • Adds python and rust integration tests + utils, that mirror each other, in their respective tests/ directory
  • Updates Makefile with commands for running different test-suite types

Related Issue

Closes #322

@bglid

bglid commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner Author

Discussion

This isn't finished @elnaske. So there are a few things I want to go through:

  • Firstly, what do you think about the organization? Does it make sense to keep a utils in each integration dir?
  • For the Rust integration tests, what do you think about adding a dev-dependency for loading .wav files? I was looking at hound. This is why it's currently some junk (but indicative) signals
  • It'd be pretty easy to refactor the current 2 integration test functions into one. Thoughts on doing that, or keeping them separated?
  • Thoughts on the Makefile updates? I can't tell if it's too much? Also, I considered adding in generating the test data, but again didn't know if it was overkill.
  • I went back and forth about having the args for the metrics in the rust tests be newtypes or not. There isn't a desired signal newtype currently, and making one for this might be a bit much, so I don't know if repurposing the noise reference one for that arg is really correct.
  • Lastly, thoughts on how to approach integration tests for rust in way that's parameterized like the python ones? I am thinking another declarative macro here.

Also, just any normal PR feedback too. I figure since there's a lot going on here probably best to go through it earlier than later.

Comment thread tests/rust/utils.rs
(clean, noisy, noise_ref)
}

#[allow(clippy::unwrap_used, clippy::indexing_slicing, reason = "Tests")]

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Whoops need to remove index slicing here

Comment thread tests/rust/utils.rs
Comment on lines +35 to +42
pub fn mse(desired: &[f64], input: &[f64]) -> f64 {
desired
.iter()
.zip(input)
.map(|(d, i)| (d - i).powi(2))
.sum::<f64>()
/ desired.len().to_f64().unwrap()
}

@bglid bglid Oct 4, 2026 •

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Need to add comments/docs to these

Comment thread tests/rust/utils.rs
Comment on lines +4 to +6
// NOTE: PLEASE READ / TODO
// This one function is slop generated and is going to be replaced
// -> by a real .wav loader. Demo purposes. Will be removed

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Disclosing the nonsense here. Chose to do this to avoid bottlenecking this overall PR for something that is going to change. I also didn't want to add hound and build around it without talking about it first.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm fine with adding it as a dev dependency

Comment on lines +29 to +31
# NOTE: Microsoft has a bug in their repo. See: https://github.com/microsoft/MS-SNSD/issues/16
# The fix is essentially to fix the '*_snrlevels' typecast as int
# See: https://github.com/microsoft/MS-SNSD/pull/11

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Worth the read lol

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

tbh if they're not maintaining it anyway we could just fork the repo and apply the fix ourselves

Comment thread pyproject.toml Outdated
requires-python = ">=3.10"
dependencies = [
"numpy>=1.21.0,<2.0.0",
"soundfile>=0.14.0",

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Same thing with hound kind of goes for this ^

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If it's only used for the tests then it should be a dev dependency

@elnaske elnaske left a comment •

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Firstly, what do you think about the organization? Does it make sense to keep a utils in each integration dir?

I think it's generally fine. My only concern is that we already have src/test_utils.rs, and I want to avoid having two test utils files for Rust. The comparison functions for floats in tests_utils could be replaced with a dev dependency (forgetting the name of the crate rn). We could then move the functions that generate the buffers for comparison into the modules where those buffers are defined (keeping them with #[cfg(test)] though)

It'd be pretty easy to refactor the current 2 integration test functions into one. Thoughts on doing that, or keeping them separated?

I'd merge them, especially if we're going to have tests for all the filter types.

I went back and forth about having the args for the metrics in the rust tests be newtypes or not. There isn't a desired signal newtype currently, and making one for this might be a bit much, so I don't know if repurposing the noise reference one for that arg is really correct.

The desired signal is the cleaned signal right? In that case we do have a type for it, but it's called OutputSignal. And I think using newtypes would be a good idea if only to make sure the tests are actually correct.

Lastly, thoughts on how to approach integration tests for rust in way that's parameterized like the python ones? I am thinking another declarative macro here.

Yeah, macros would be the best solution AFAIK

Think I addressed the other points in the comments

Comment thread .gitignore Outdated
Comment thread Makefile Outdated
Comment thread Makefile Outdated
Comment thread pyproject.toml Outdated
requires-python = ">=3.10"
dependencies = [
"numpy>=1.21.0,<2.0.0",
"soundfile>=0.14.0",

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

If it's only used for the tests then it should be a dev dependency

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Since most of this is running terminal commands, do you think you could make it a bash file instead?

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Yeah that's a good call I'll change that

Comment on lines +29 to +31
# NOTE: Microsoft has a bug in their repo. See: https://github.com/microsoft/MS-SNSD/issues/16
# The fix is essentially to fix the '*_snrlevels' typecast as int
# See: https://github.com/microsoft/MS-SNSD/pull/11

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

tbh if they're not maintaining it anyway we could just fork the repo and apply the fix ourselves

Comment thread tests/rust/utils.rs
Comment on lines +4 to +6
// NOTE: PLEASE READ / TODO
// This one function is slop generated and is going to be replaced
// -> by a real .wav loader. Demo purposes. Will be removed

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I'm fine with adding it as a dev dependency

Comment thread tests/rust/filters.rs
Comment on lines +1 to +4
#![allow(
clippy::tests_outside_test_module,
reason = "This crate is exclusively an integration test target"
)]

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

You'll probably want to mark this module with #[cfg(test)]. Should get rid of the lint warning and ensures that it's not compiled in release mode.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Actually looked up the docs and they don't have it, so idk

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Ik this seems like a weird gap

Comment thread tests/rust/main.rs

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Do we even need a main.rs? Docs for integration tests don't have it.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

Let me try without it. I think I was getting weird errors beforehand because of it being nested in tests/rust as opposed to just tests/

@elnaske

elnaske commented Oct 4, 2026

Copy link
Copy Markdown
Collaborator

Actually checked out the branch locally and it looks like the data directory is over 5 GB. In that case, wouldn't it make more sense to keep a few wav files as fixtures?

@elnaske

elnaske commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator

Also the step size, at least for LMS is pretty low. I tried 0.01 like in the tests and you can barely hear a difference. Jacking it up to at least 0.1 works much better

Edit: Checked out the other ones too. Can't get good params for NLMS and Block LMS, so we might have to double check the source code for correctness (or it could be that they just don't work well for this one signal, idk). RLS works fine though.

Edit 2: Since the tests pass anyway, I think it might be a good idea to set minimum thresholds for the SNR. I.e. there needs to be a minimum improvement of x%. Or is that what the MSE tests are for, to check that the output is closer to the clean signal than then the input?

@elnaske

elnaske commented Oct 4, 2026

Copy link
Copy Markdown
Collaborator

Another thing I found: running uv run pytest directly (not from make) returns some ModuleNotFoundErrors for the integration tests. Also we could set testpaths in pyproject.toml under the pytest options so it only checks the dirs we want

@bglid

bglid commented Oct 4, 2026

Copy link
Copy Markdown
Owner Author

The desired signal is the cleaned signal right? In that case we do have a type for it, but it's called OutputSignal. And I think using newtypes would be a good idea if only to make sure the tests are actually correct.

Out atm so only can respond to this one but the desired signal wrt to these tests the desired signal would be the OG clean signal.

@elnaske

elnaske commented Oct 4, 2026 •

Copy link
Copy Markdown
Collaborator

Out atm so only can respond to this one but the desired signal wrt to these tests the desired signal would be the OG clean signal.

In that case I'd just use a slice. No use in adding new types if they're only for tests imo

Edit: In the legacy Python code, the docstring for FilterModel.filter() says this:

d (NDArray[np.float64]):
    "Desired Signal", which in the ANC use-case is the noisy input signal.

@bglid

bglid commented Oct 5, 2026

Copy link
Copy Markdown
Owner Author

Out atm so only can respond to this one but the desired signal wrt to these tests the desired signal would be the OG clean signal.

In that case I'd just use a slice. No use in adding new types if they're only for tests imo

Edit: In the legacy Python code, the docstring for FilterModel.filter() says this:

d (NDArray[np.float64]):
    "Desired Signal", which in the ANC use-case is the noisy input signal.

In that case I'll change the name in the integration tests to original_wav, essentially that's all it is in these tests. It's just for calculating the MSE and SNR. In adaptive filter lit. they use desired signal as the label for the signal that's attempting to be approximated. It's confusing lol

@bglid

bglid commented Oct 5, 2026

Copy link
Copy Markdown
Owner Author

Also the step size, at least for LMS is pretty low. I tried 0.01 like in the tests and you can barely hear a difference. Jacking it up to at least 0.1 works much better

Edit: Checked out the other ones too. Can't get good params for NLMS and Block LMS, so we might have to double check the source code for correctness (or it could be that they just don't work well for this one signal, idk). RLS works fine though.

Edit 2: Since the tests pass anyway, I think it might be a good idea to set minimum thresholds for the SNR. I.e. there needs to be a minimum improvement of x%. Or is that what the MSE tests are for, to check that the output is closer to the clean signal than then the input?

This points to we may want to just have a few pre-set wavs to test on where we know how it'll behave. One of the characteristics of adaptive filters is that they can be finicky, signal/noise dependent too. You'll find that the $\mu can sometimes need to be pretty low. Idk if you recall, in that example I presented with that bela board, the input params for an NLMS filter was:

	// NLMS filter
	gNLMS.setup(32, 0.0001f, 0.000001);    // window_size, mu, eps

Sometimes they can take a second or two to converge. It's easy to accidentally diverge on them.

@bglid

bglid commented Oct 5, 2026

Copy link
Copy Markdown
Owner Author

Actually checked out the branch locally and it looks like the data directory is over 5 GB. In that case, wouldn't it make more sense to keep a few wav files as fixtures?

Wrt to other comment, I think this is smart to do. I suppose what's the best way to do this? I don't want git to track it, so what about forking the MS repo, generating a handful of .wav files, then removing most of the unneeded slop from the repo, then having the .sh setup file pull those files into the /data directory upon command?

@elnaske

elnaske commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

Wrt to other comment, I think this is smart to do. I suppose what's the best way to do this? I don't want git to track it, so what about forking the MS repo, generating a handful of .wav files, then removing most of the unneeded slop from the repo, then having the .sh setup file pull those files into the /data directory upon command?

Actually I was just thinking keeping them as fixtures in the repo. We only need a few short wav files, so it shouldn't take up to much space.

@elnaske

elnaske commented Oct 5, 2026 •

Copy link
Copy Markdown
Collaborator

This points to we may want to just have a few pre-set wavs to test on where we know how it'll behave. One of the characteristics of adaptive filters is that they can be finicky, signal/noise dependent too. You'll find that the $\mu can sometimes need to be pretty low. Idk if you recall, in that example I presented with that bela board, the input params for an NLMS filter was:

	// NLMS filter
	gNLMS.setup(32, 0.0001f, 0.000001);    // window_size, mu, eps

Sometimes they can take a second or two to converge. It's easy to accidentally diverge on them.

In that case we'll want to manually test parameters to find ones that work well with the input. I see that the Python CI is currently failing because the SNR after block LMS is slightly worse, so we'll want good parameters to avoid this. I want the results of the integration to tests to be really clear cut so that we don't run the risk of them failing due to chance.

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.

Integration Tests

2 participants