Skip to content

Overhaul app launching, unit tests #4115

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

Merged
merged 16 commits into from
May 5, 2021
Merged

Overhaul app launching, unit tests #4115

merged 16 commits into from
May 5, 2021

Conversation

ferdnyc
Copy link
Contributor

@ferdnyc ferdnyc commented Apr 30, 2021

(Replacing #4114)

This PR, tested on both Linux and Windows, completely revamps the initial creation of the QApplication instance and the MainWindow class, as OpenShot is being started by launch.py (also significantly rewritten), all in the service of one central goal: completely divorce the application setup (which is done in OpenShotApp's __init__, same as always) from the interface startup, which now lives in a new OpenShotApp.gui() method. Calling that is optional, and the unit tests simply... don't.

When launching the unit tests (from tests/query_tests.py), the interface is never even started, and therefore (by implication) neither is the web backend. Which alleviates the problems caused by QtWebEngine not shutting down properly because it hasn't even had a chance to start yet before the unit tests are completed.

I've also moved all of that OpenGL setup stuff and other housekeeping out of the top of classes.app and put it in launch.py instead, so that it's truly done BEFORE creating the QApplication (or, OpenShotApp) instance. I've always been wary of the fact that we were effectively doing it "while" the instance was being created, not technically "before".

And having it in launch.py means the unit tests can also skip that stuff. It's now possible, in fact, to run the unit tests in a headless environment without needing xvfb, just by adding a -platform minimal or -platform offscreen on the command line for query_tests.py.

(The platform plugin has to be changed because Qt's default on Linux is xcb, which still expects an X display even if nothing makes use of it.) Edit: It seems from the Github Actions CI that offscreen still expects some sort of display output or server that isn't present. -platform minimal works great even in the... well... minimal CI container environments, though. Whoda thunk?

There were some other changes involved, like deferring any QMessageBox calls that previously occurred during OpenShotApp.__init__(). Now they're placed on a queue, and not shown until requested by launch.py (or automatically, if there are any outstanding when OpenShotApp.gui() is called.

Other changes

  • I've completely removed classes.settings.get_settings(). Settings should now be requested from the app instance (because it holds the settings instance), using a path along the lines of get_app().get_settings(). There is also a convenience function get_settings() in classes.app, mirroring the old one in classes.settings, but you shouldn't use classes.app.get_settings() either. It just makes more sense to go through the application instance.

Next steps

GUI-free Model Classes

The next obvious evolutionary step, here, would be to move the data models out of MainWindow, and have them be both initialized and held by the OpenShotApp instance. There's no reason our data models should be dependent on a GUI being launched, and if they weren't then we could start writing unit tests for them, which would be a very good thing.

For that to happen, though, all of the hidden GUI dependencies in them have to be shaken out. That means no calls to get_app().window.anything anywhere in the models. Right now they contain way too many sneaky dependency inversions like this (from the files proxy model):

    def filterAcceptsRow(self, sourceRow, sourceParent):
        """Filter for text"""
        if get_app().window.actionFilesShowVideo.isChecked() \
                or get_app().window.actionFilesShowAudio.isChecked() \
                or get_app().window.actionFilesShowImage.isChecked() \
                or get_app().window.filesFilter.text():

The way that should work — both for model integrity and to be far more efficient — is to have the main window class connect the changed signal from the QActionGroup holding the selector buttons to a slot in the model class that stores the selected filter option as an instance variable, which defaults to no filtering.

That way, the model doesn't need to consult the GUI just to do its job, and if there isn't a GUI then the filtering will never be activated. (Though it still could be, programmatically, in the unit tests. All they'd have to do is simply connect signals to that slot and emit() them.) Properly isolated, the existence of an actual GUI should be completely irrelevant to the group-filtering features of the files model. It certainly shouldn't be an assumption baked into the model that crashes its code when the GUI isn't available.

ferdnyc added 10 commits March 23, 2021 04:31
- classes.app will have a get_settings() function (which we should
  try to migrate to, from classes.settings.get_settings()
- in addition, the OpenShotApp instance has a get_settings() METHOD
  which is most-preferable
  (`from classes.app import get_app; s = get_app().get_settings()`)
- Massively re-work the application setup, so that *far* less is done
  in the __init__() for OpenShotApp. In particular...
  * All GUI setup is deferred to a separate `.gui()` method, which is
    now optional (the application instance can be created without
    automatically creating the full GUI).
  * A new `StartupMessage` class is created, and any message to be
    displayed in a QMessageBox is now stored in an object instance
    and placed on a queue. OpenShotApp.gui() will automatically
    call the app's `.show_errors()` method to process the queue,
    but it can also be called before/without `.gui()`.
  * The app instance's references to `info` and `openshot` are
    non-global, so they're passed into several methods which
    need access to them (just something to look out for)

- `launch.py` is also re-worked in accordance with the new
  design of classes.app.OpenShotApp

- Multiple media files can now be passed as separate arguments
  on the command line, and they will be added to the initial
  project in order. The number of positional arguments is no
  longer capped at 1 (except in the case of an `.osp` file)
- Take advantage of the new, more compartmentalized OpenShotApp class,
  and run the unit tests without creating the application GUI at all.
- Unit tests can even be run headless by selecting a platform
  plugin with no display requirements on the command line, e.g.:
  `python3 tests/query_tests.py --platform minimal`
  `python3 tests/query_tests.py --platform offscreen`
Settings should always be requested from the app instance, which
holds the settings object: `get_app().get_settings()`
@ferdnyc ferdnyc added interface GUI / user interface issues (i.e. windows, buttons, scrolling, pop-ups, etc...) 📦 packaging An issue or PR related to the official package builds labels Apr 30, 2021
@jonoomph
Copy link
Member

LGTM! Nice work! 😀

@jonoomph
Copy link
Member

jonoomph commented May 5, 2021

@ferdnyc Is this one ready to merge?

@ferdnyc
Copy link
Contributor Author

ferdnyc commented May 5, 2021

@jonoomph AFAICT, yes, but maybe you want to test the Mac build first just in case?

@jonoomph
Copy link
Member

jonoomph commented May 5, 2021

Sure, I can give it a quick look on Mac!

@jonoomph
Copy link
Member

jonoomph commented May 5, 2021

As far as I can tell, works fine on the Mac! Merge away!

@ferdnyc ferdnyc merged commit 0ceb52d into develop May 5, 2021
@ferdnyc ferdnyc deleted the app-launch2 branch May 5, 2021 19:16
@ferdnyc
Copy link
Contributor Author

ferdnyc commented May 5, 2021

@jonoomph Merged! So that should be the end of the spurious unit test failures.

@ferdnyc
Copy link
Contributor Author

ferdnyc commented May 5, 2021

@jonoomph

P.S> I'm about 2/3 of the way through this...

GUI-free Model Classes

The next obvious evolutionary step, here, would be to move the data models out of MainWindow, and have them be both initialized and held by the OpenShotApp instance. There's no reason our data models should be dependent on a GUI being launched, and if they weren't then we could start writing unit tests for them, which would be a very good thing.

For that to happen, though, all of the hidden GUI dependencies in them have to be shaken out. That means no calls to get_app().window.anything anywhere in the models. Right now they contain way too many sneaky dependency inversions like this (from the files proxy model):

    def filterAcceptsRow(self, sourceRow, sourceParent):
        """Filter for text"""
        if get_app().window.actionFilesShowVideo.isChecked() \
                or get_app().window.actionFilesShowAudio.isChecked() \
                or get_app().window.actionFilesShowImage.isChecked() \
                or get_app().window.filesFilter.text():

The way that should work — both for model integrity and to be far more efficient — is to have the main window class connect the changed signal from the QActionGroup holding the selector buttons to a slot in the model class that stores the selected filter option as an instance variable, which defaults to no filtering.

That way, the model doesn't need to consult the GUI just to do its job, and if there isn't a GUI then the filtering will never be activated. (Though it still could be, programmatically, in the unit tests. All they'd have to do is simply connect signals to that slot and emit() them.) Properly isolated, the existence of an actual GUI should be completely irrelevant to the group-filtering features of the files model. It certainly shouldn't be an assumption baked into the model that crashes its code when the GUI isn't available.

I just have about a solid afternoon's work left, fixing up all of the view classes. So, realistically, given the pace I've been managing lately... probably another week or two. (Especially now that a tenant moved out yesterday with zero notice, so the rest of this week is suddenly booked up with turning that apartment back around for the realtor.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
interface GUI / user interface issues (i.e. windows, buttons, scrolling, pop-ups, etc...) 📦 packaging An issue or PR related to the official package builds
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants