Using Catch2 but need a MessageLoop

I’m using Catch2 as my unit test framework, which for most cases has worked pretty good so far. Some JUCE classes need a running MessageLoop to work properly, so need to create one on startup of the test executable. From what I can tell, I have to options:

1

Use a JUCE GuiApplication as the test runner. Catch2 needs argc & argv, so I would need to get them from somewhere. Not sure how. Since it would be a GUI application, I would also need extra setup on the docker test runners to fake a display.

2

Manually start the MessageLoop. This is what I’ve been doing so far, but I’m not sure if I’m doing it correctly:

#define CATCH_CONFIG_RUNNER
#include "catch2/catch.hpp"

int main(int argc, char* argv[])
{
    auto gui = juce::ScopedJuceInitialiser_GUI {};
    return Catch::Session().run(argc, argv);
}

Ideally I’d like to be able to test plugins this way, so setting parameters & running the processBlock method. I’ve had some weird results where I was setting a parameter to a new value but it still contained the old value when checking. Any suggestions?

4 Likes

What you are doing in method number 2 is essentially what I do (I use google test though not catch).

If you are using cached values then that might explain the issue with the parameter not updating. I have had some issues dealing with value tree updates and cached values in my unit tests. If you are using cached values forcing the update of the cached value might work. You might take a look at this thread and check out Daves answer. I ended up using an async updater and calling handleUpdateNowIfNeeded in my tests and that seemed to work well.

1 Like

I appreciated this thread!

Even trivial tests like this Catch2 example will hit JUCE_ASSERT_MESSAGE_MANAGER_EXISTS if there’s an APVTS involved in the processor’s constructor.

TEST_CASE("Plugin instance name", "[plugin]") {
    MyAudioProcessor testPlugin;
    CHECK_THAT( testPlugin.getName().toStdString(), Catch::Equals( "My Audio Processor" ) );
}
1 Like

+1 for method 2 here.

Previously I had put the juce::ScopedJuceInitialiser_GUI inside the TEST_CASE block but that seems to cause memory leaks.

Putting the GUI initialiser at the top of main() seems to have plugged the leaks.

3 Likes

I wonder why it would cause leaks… The RAII mechanism should take care of it, but perhaps catch’s TEST_CASE expands to some kind of lambda with weird scoping/capturing or something…

1 Like