Skip to content
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

Add basic tests for the ScreenshotDialog widget and fixes #6057

Merged
merged 22 commits into from Aug 19, 2023

Conversation

dalthviz
Copy link
Member

@dalthviz dalthviz commented Jul 13, 2023

Fixes/Closes

closes #6078

Description

Adds some tests to the ScreenshotDialog as a continuation of the effort started at #5864 to increase Qt widgets code coverage and fixes some errors with the non-native version (the one used when launching napari from and IPython console)

Notes and questions

  • The tests call the dialog show method and also sets the non-native option so the tests here only cover the case when napari is being launched from an IPython console. The testing of the OS native version of the dialog is more involved.
  • Checking the code related with the dialogs available I noticed that some of them (files/widgets classes) have the qt_* /Qt prefix or the _dialog/Dialog suffix but others don't (for example screenshot_dialog.py/ScreenshotDialog vs qt_about/QtAbout). Is that something worthy to standardize or maybe there is already some logic behind the naming?

Type of change

  • Tests improvements/additions

How has this been tested?

  • all tests pass with my change
  • I check if my changes works with both PySide and PyQt backends
    as there are small differences between the two Qt bindings.

Final checklist:

  • My PR is the minimum possible work for the desired functionality

@dalthviz dalthviz added tests Something related to our tests qt Relates to qt maintenance PR with maintance changes, labels Jul 13, 2023
@dalthviz dalthviz self-assigned this Jul 13, 2023
@codecov
Copy link

codecov bot commented Jul 13, 2023

Codecov Report

Merging #6057 (b833681) into main (670c971) will increase coverage by 0.04%.
The diff coverage is 98.18%.

@@            Coverage Diff             @@
##             main    #6057      +/-   ##
==========================================
+ Coverage   91.62%   91.67%   +0.04%     
==========================================
  Files         579      580       +1     
  Lines       50749    50800      +51     
==========================================
+ Hits        46498    46569      +71     
+ Misses       4251     4231      -20     
Files Changed Coverage Δ
napari/_qt/dialogs/screenshot_dialog.py 93.10% <75.00%> (+58.62%) ⬆️
...apari/_qt/dialogs/_tests/test_screenshot_dialog.py 100.00% <100.00%> (ø)

... and 1 file with indirect coverage changes

@dalthviz dalthviz marked this pull request as draft July 13, 2023 16:56
@andy-sweet andy-sweet added this to the 0.5.0 milestone Jul 13, 2023
@dalthviz dalthviz marked this pull request as ready for review July 14, 2023 18:15
@brisvag
Copy link
Contributor

brisvag commented Jul 24, 2023

@dalthviz sorry this has been hanging so long! Maybe @Czaki could review, qt tests are always tricky :/

@brisvag brisvag requested a review from Czaki July 24, 2023 15:48
@dalthviz
Copy link
Member Author

No problem @brisvag ! In fact I was rechecking this and updating it since seems like there is an issue about the screenshot dialog (#6078) which I'm able to reproduce locally but the tests are not picking up 🤔

@dalthviz dalthviz changed the title Add basic tests for the ScreenshotDialog widget Add basic tests for the ScreenshotDialog widget and fixes Jul 24, 2023
@psobolewskiPhD psobolewskiPhD self-requested a review July 24, 2023 19:39
Co-authored-by: Grzegorz Bokota <bokota+github@gmail.com>
Comment on lines 69 to 71
if base_result:
self.save_function(save_path)
return base_result
Copy link
Collaborator

Choose a reason for hiding this comment

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

Wait. It looks like I was wrong here. Base on Qt documentation the accept() should return None (void in c++) https://doc.qt.io/qt-6/qdialog.html#accept

Suggested change
if base_result:
self.save_function(save_path)
return base_result
self.save_function(save_path)
return base_result

If we would like to validate then we should depend on self.result()

Copy link
Member Author

@dalthviz dalthviz Jul 25, 2023

Choose a reason for hiding this comment

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

Oh totally right! So the validation should be something like: if self.result() == QDialog.DialogCode.Accepted: maybe?

Copy link
Collaborator

Choose a reason for hiding this comment

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

maybe just if self.result()

dalthviz and others added 2 commits July 25, 2023 11:15
Co-authored-by: Grzegorz Bokota <bokota+github@gmail.com>
Copy link
Collaborator

@Czaki Czaki left a comment

Choose a reason for hiding this comment

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

Few remarks

monkeypatch.setattr(QMessageBox, "warning", overwrite_message)

dialog = ScreenshotDialog(
save_function, directory=str(tmp_path), history=get_save_history()
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why do we use get_save_history here? Why not an empty list?

Copy link
Member Author

Choose a reason for hiding this comment

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

Will change it! I think what confused me was that in the ScreenshotDialog constructor the default value for the history kwarg is None

napari/_qt/dialogs/_tests/test_screenshot_dialog.py Outdated Show resolved Hide resolved
napari/_qt/dialogs/_tests/test_screenshot_dialog.py Outdated Show resolved Hide resolved
napari/_qt/dialogs/_tests/test_screenshot_dialog.py Outdated Show resolved Hide resolved
napari/_qt/dialogs/_tests/test_screenshot_dialog.py Outdated Show resolved Hide resolved
Comment on lines 27 to 34
def handle_qt_overwrite_message():
for widget in QApplication.topLevelWidgets():
if isinstance(widget, QMessageBox):
# test should not enter here!
widget.accept()
nonlocal qt_overwrite_shown
qt_overwrite_shown = True
break
Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Member Author

Choose a reason for hiding this comment

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

I ended up with this logic since I tried patching the warning method as in the link but the patch was not being triggered. Maybe patching exec could work 🤔. My initial guess was that since the warning QMessageBox is instantiated in Qt code (C++) the monkey patching didn't have any effect on it but I will check!

Copy link
Member Author

Choose a reason for hiding this comment

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

Checked and seems like even when monkey patching exec the Qt generated QMessageBox warning message keeps appearing.

If I do the monkey patch and revert the changes, to first call super().accept and then the save_function in the accept method for the dialog, the test hangs until I manually close the Qt generated QMessageBox warning:

imagen

Maybe I'm missing something in the monkey patch procedure @Czaki ? 🤔

@pytest.mark.parametrize("filename", ["test", "test.png", "test.tif"])
def test_screenshot_save(qtbot, tmp_path, monkeypatch, filename):
    """Check passing different extensions with the filename."""

    def save_function(path):
        # check incoming path has extension event when a filename without one
        # was provided
        assert filename in path
        assert "." in filename or ".png" in path

        # create a file with the given path to check for
        # non-native qt overwrite message
        with open(path, "w") as mock_img:
            mock_img.write("")

    def qt_overwrite_qmessagebox_warning(*_, **__):
        raise RuntimeError("exec_ call")

    monkeypatch.setattr(QMessageBox, "exec_", qt_overwrite_qmessagebox_warning)
    monkeypatch.setattr(QMessageBox, "critical", qt_overwrite_qmessagebox_warning)
    monkeypatch.setattr(QMessageBox, "information", qt_overwrite_qmessagebox_warning)
    monkeypatch.setattr(QMessageBox, "question", qt_overwrite_qmessagebox_warning)
    monkeypatch.setattr(QMessageBox, "warning", qt_overwrite_qmessagebox_warning)

    # setup dialog
    dialog = ScreenshotDialog(
        save_function, directory=str(tmp_path), history=[]
    )
    qtbot.addWidget(dialog)
    dialog.setOptions(QFileDialog.DontUseNativeDialog)
    dialog.show()

    # check dialog and set filename
    assert dialog.windowTitle() == 'Save screenshot'
    line_edit = dialog.findChild(QLineEdit)
    line_edit.setText(filename)

    # check that no warning message related with overwriting is shown
    dialog.accept()

    # check the file was created
    save_filename = filename if '.' in filename else f'{filename}.png'
    qtbot.waitUntil(lambda: (tmp_path / save_filename).exists())

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh. So it looks like Your procedure was better as maybe Qt uses inner references.

Co-authored-by: Grzegorz Bokota <bokota+github@gmail.com>
@dalthviz dalthviz added bugfix PR with bugfix and removed maintenance PR with maintance changes, labels Aug 10, 2023
Copy link
Member

@psobolewskiPhD psobolewskiPhD left a comment

Choose a reason for hiding this comment

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

Thanks @dalthviz
I think the bug fix makes this for 0.4.19?

@psobolewskiPhD psobolewskiPhD modified the milestones: 0.5.0, 0.4.19 Aug 11, 2023
dalthviz and others added 2 commits August 14, 2023 09:57
@Czaki Czaki added the ready to merge Last chance for comments! Will be merged in ~24h label Aug 14, 2023
@jni jni merged commit 8294095 into napari:main Aug 19, 2023
35 checks passed
@jni jni removed the ready to merge Last chance for comments! Will be merged in ~24h label Aug 19, 2023
Czaki added a commit that referenced this pull request Oct 17, 2023
# Fixes/Closes

closes #6078 

# Description

Adds some tests to the `ScreenshotDialog` as a continuation of the
effort started at #5864 to increase Qt widgets code coverage and fixes
some errors with the non-native version (the one used when launching
napari from and IPython console)

## Notes and questions

* The tests call the dialog `show` method and also sets the non-native
option so the tests here only cover the case when napari is being
launched from an IPython console. The testing of the OS native version
of the dialog is more involved.
* Checking the code related with the dialogs available I noticed that
some of them (files/widgets classes) have the `qt_*` /`Qt` prefix or the
`_dialog`/`Dialog` suffix but others don't (for example
`screenshot_dialog.py/ScreenshotDialog` vs `qt_about/QtAbout`). Is that
something worthy to standardize or maybe there is already some logic
behind the naming?

---------

Co-authored-by: Grzegorz Bokota <bokota+github@gmail.com>
Co-authored-by: Genevieve Buckley <30920819+GenevieveBuckley@users.noreply.github.com>
Co-authored-by: Juan Nunez-Iglesias <jni@fastmail.com>
Co-authored-by: Peter Sobolewski <76622105+psobolewskiPhD@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bugfix PR with bugfix qt Relates to qt tests Something related to our tests
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[ipython] File > Save Screenshot... warns about replacing if extension is provided
7 participants