The following code hits an assert. I would expect that I can use this property class that way with short lifetimes.
juce::PropertiesFile::Options options;
options.applicationName = "Test";
options.filenameSuffix = "xml";
options.folderName = "ToguAudioLine";
options.storageFormat = juce::PropertiesFile::storeAsXML;
options.ignoreCaseOfKeyNames = true;
options.osxLibrarySubFolder = "Application Support";
options.millisecondsBeforeSaving = -1; // edit: assert still there also with this line
std::unique_ptr<ApplicationProperties> properties = std::make_unique<ApplicationProperties>();
properties->setStorageParameters(options);
properties->getUserSettings()->setValue("Key", "value");
properties->getUserSettings()->save();
properties = nullptr; // <- multiple assertions here
Assert is here:
~TimerThread() override
{
// If this is hit, a timer has outlived the platform event system.
jassert (MessageManager::getInstanceWithoutCreating() != nullptr);
Anything I can do about it? I need this.
I wonder why we need a timer for this “saveIsNeeded” call. Can’t we use save and have some control over what happens?
Edit: Some way to disable the timer?
Edit2: Tested on macOS
That likely means you have a Timer that has static storage duration. Either you have a static object that is-a or has-a Timer. It’s generally best practice to avoid static objects if you can. That being said some changes may be coming soon that means you don’t hit this assert in future.
Is your application exiting when this happens? I think I misread the assert the first time. Maybe you need to delete the properties object you created?
Just looking again PropertiesFile is a Timer so when is it being created/destroyed? based on the assert there is no MessageManager when you’re destroying it, which as above normally suggests some kind of static object.
Thanks for the info. This is the timer in the juce::ApplicationProperties. I don’t have any statics. Just the code in the first post. All juce classes.
Assuming the assertion is from the Timer in PropertiesFile then it suggests when the destructor is run there is no MessageManager. Either one never gets created or it’s already been destroyed which suggests the code is running very late on.
If there isn’t a MessageManager then Timer callbacks can’t be delivered.
I don’t have any application. Only the code above is in a catch test. It’s a test case. I want to test serial verification and need to prepare the properties file. But I think this also happens in other cases.
That’s your problem then as there won’t be a MessageManager to deliver any messages and a Timer needs a MessageManager.
That being said I may merge some work soon that would get rid of that assertion, but to be clear any internal methods that rely on the Timer might fail, or hang, I haven’t looked.
I have an internal branch I’ve been working on that might allow us to mock the MessageManager in the future for exactly this reason but I’m afraid it won’t be the focus of my time for a little while yet.
Thank you for doing this, it’s great news. I’m happy to hear that the JUCE team thinks about this. It would make life a lot easier for me and hopefully also others
You can use (from memory) a ScopedJuceInitializer in your test, which will at least create a message manager. But if I remember correctly (take all this with multiple grains of salt) that alone will not run the message thread and actually process the queue. So if your test doesn’t depend on asynchronous messages being processed, that could work for you. Otherwise I think you need to somehow run a message thread with your test (which should also be possible, but I haven’t done it yet).
Thanks for the idea. Luckily it saves the property file if I set:
options.millisecondsBeforeSaving = -1;
The assertions are annoying when debugging, but the tests work.
Thanks for the Idea. This would be the last resort. I don’t think it makes sense to run a real timer in a test. Tests should be fast and run in a single thread. Tests with timers and other threads are usually not reliable.
I would implement a timer class with interface that accepts a function for the callback and pass a reference to the class I want to test. No inheritance and easily replaceable with different implementations. This way I can insert a mock for tests and trigger the timer callbacks directly in the test.
Unfortunately, this is not possible for existing juce classes that already use a timer or depend on other singletons. I think the best option for the moment is, to extract business logic in separate testable classes if possible.