Unit Tests: Custom Projucer Target for Audio Plugins

Edit: A patch for the Projucer is available a little further down

At our company we work with JUCE to create cross-platform audio plugins. We started to integrate unit tests and so far this has been rather tedious to setup.
Basically what we do: We have a project that compiles the plugin as VST /AU / etc… Then there’s another Console-App project that simply runs our unit tests from the same codebase (which are done with the googletest / gtest framework)

Now here’s an idea: We would like to add another build target to the projucer (much like suggested here). And I’d like to ask for advice on where to look in the projucer codebase or how to proceed generally.

I’ve seen the ProjectType class and it seems to me, I’d have to add another entry into the Target::Type enum - something like Target::Type::gtestStandaloneApp. Then, I’d have to add a couple of other lines here and there to make this visible throughout the app.
My idea was to take the current standalone app target as a foundation and simply throw out the existing code and add to main() the single call into the googletest framework that runs all tests.
However, I can track the use of the Target::Type enum throughout the Projucer codebase, but I have yet to find where the code for the standalone app target actually comes from. There’s no template in the BinaryData and I haven’t seen any hints in the code at all.

Any advice would be appreciated.

EDIT: Some more hints, in case someone else is interested:

  • LibraryModule::CompileUnit::isNeededForExporter() could be used to assign code files to the unit test exporter only, e.g. by sorting out files with a suffix like _gtest - just like it is done there for the other build targets as well.
  • the code for the standalone app is found in the juce module audio_plugin_client/Standalone/*

Just giving my opinion here:

  • The Projucer is a nice tool to get started quickly, but will never be complex enough to handle all use cases of professional developers. Your use case is just one of many that is not covered yet.
  • I would suggest you (like we do in my company), to move away from the Projucer and use more customisable build tools (like CMake). It is tedious to convert a Juce plugin made with the Projucer to Cmake, but the result is a much easier to customise build process.

It’s not that tedious if you use FRUT :wink: (I’m the author).

Oh that’s cool!

1 Like

Thanks for suggesting alternatives to the Projucer. Right now, it has served us well. We did our integration with custom python build scripts that generate any required ressources, then re-save the project via the projucer command line interface and finally do the build and all the post processing.

I’m a little afraid to switch to yet another tool (cmake) which we don’t yet use. From the FRUT documentation I see that it supports almost all settings the projucer has - so migration shouldn’t be too much hassle. But how would we go about adding our own custom targets here? What is the main benefit from using it over a modified Projucer?

BTW, I have already modded Projucer to add an additional build target and it mostly works, except for the include paths that are required for googletest - but I’ll figure that out as well.

1 Like

I totally understand that feeling, so I won’t encourage you to use FRUT (and CMake) unless you really want to.

Currently FRUT only provides Reprojucer.cmake, which tries to reproduce what Projucer does, so the easiest way is to combine projects (for instance an Audio Plug-In project and a Console Application project). @vallant asked a similar question in a GitHub issue (https://github.com/McMartin/FRUT/issues/490) and I made a branch (https://github.com/McMartin/FRUT/tree/example-for-issue-490) to show how to do it.

The main benefit is that there is no need to change JUCE/Projucer. One can just use the official JUCE code, without any modifications, and let others (myself for FRUT :slightly_smiling_face:) deal with build system related stuff.

@sparklingRhubarb You seem to be comfortable with modding Projucer, so you should keep on doing that as long as it fits your needs. I spent a lot of time reading the code of Projucer to implement FRUT, so feel free to ask me anything by PM if you’re stuck on something.

After some more evaluation (and distraction by other work) we finally decided to go with the modded Projucer instead of a cmake solution. For anyone interested, here is a patch that can be applied to JUCE 5.4.3, with the following changes:

JUCE_543_AudioPlugin_GoogleTestTarget.zip (917.6 KB)

  • Added GoogleTestConsoleApp as a new plugin format in the projucer
  • Added googletest sources to the juce_audio_plugin_client module
  • adjusted XCode and Visual Studio exporters to
    • assign all files with a basename ending on _gtest to the GoogleTestConsoleApp target and exclude them from the other targets
    • link the SharedCode target to the GoogleTestConsoleApp target so that a full plugin can be instantiated as part of the tests
    • define the JUCE_CURRENT_TARGET_IS_GTEST_CONSOLE_APP preprocessor macro in the corresponding build target

Now the workflow is like this:

  1. Create an audio plugin project with the modded projucer
  2. Set the checkmark for the GoogleTestConsoleApp target
  3. Add your test source code as cpp files to any folder in the project, but use this naming scheme: <mytestfile>_gtest.cpp. Those files will only be built as part of the GoogleTestConsoleApp target and will be excluded from the other build targets (this follows the convention for the other special targets like VST, AU, etc where the file ending also determines to which target the file will be added). The google test headers will be included with the juce header when this target is built.
  4. save the project and open it in XCode or Visual Studio
  5. you will find that there is now a new build target that builds a console app. This app will run all tests that were found in your *_gtest.cpp sourcecode files. Under Visual Studio, they will also appear in the test explorer panel so you can run tests individually.

I’m open to feedback on this. It would be great if something like this could be part of JUCE natively. It would certainly lower the setup hassles and make it easier to develop test driven plugins with JUCE.