-
-
Notifications
You must be signed in to change notification settings - Fork 3k
[WIP] Put the PDFAnnotationImporter under Test, enhance FileAnnotationTab #2640
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
Conversation
* Fixed localization Issues
…Annotation and EntryAnnotationImporterTest
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah more tests! 😄
In general the code looks good. I just have two questions and some remarks concerning the tests. Can be merged after, so I'll remove the "ready-for-review" tag.
| public Map<String, List<FileAnnotation>> getFromCache(BibEntry entry) throws ExecutionException { | ||
| return annotationCache.get(entry); | ||
| public Map<String, List<FileAnnotation>> getFromCache(BibEntry entry) { | ||
| LOGGER.info(String.format("Loading Bibentry '%s' from cache.", entry.getField(BibEntry.KEY_FIELD).get())); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe change to Logger.debug?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in 2894c9e
| public void noAnnotationsWriteProtected() { | ||
|
|
||
| List<FileAnnotation> annotations = importer.importAnnotations(Paths.get("src/test/resources/pdfs/write-protected.pdf")); | ||
| assertEquals(0, annotations.size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it is preferable to check versus an empty list: assertEquals(Collections.emptyList(), annotations). In the case, when the test fails you get a more helpful error message.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in c4aed22
| @Test | ||
| public void inlineNoteMinimal() { | ||
| List<FileAnnotation> annotations = importer.importAnnotations(Paths.get("src/test/resources/pdfs/minimal-inlinenote.pdf")); | ||
| assertEquals(1, annotations.size()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As above, check versus an expected list:
FileAnnotation expected = new ..... and assertEquals(Collections.singletonList(expected), annotations))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not so useful, as a FileAnnotation is a quite complex class (especially if it has a linkedAnnotation) and some fields are just not of interest for the tests. I would suggest to leave it as it is. Furthermore, the message from JUnit is more helpful if I check each field separately.,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The junit message would show the objects values for exepcted vs actual as it is done in the fetchers or importers for example.
So you can directly compare them and see any differences, here you would get only Expected 1 actual 0 in case a test would fail -> Not helpful
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hm. After @tobiasdiez comment I have tried it already but the message is: (see 8ed6abf)
java.lang.AssertionError: Comparison fails. WHY? expected: java.util.Collections$SingletonList<[inline note annotation]> but was: java.util.LinkedList<[inline note annotation]>
Expected :java.util.Collections$SingletonList<[inline note annotation]>
Actual :java.util.LinkedList<[inline note annotation]>
First of all I don't get why the comparison fails.
Second the message is really not helpful.
Surely I made some kind of mistake, but I don't understand what's the issue here. Can you give me a hint?
| } | ||
| } | ||
|
|
||
| private String delocalize(String content) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why does the content is first localized and then delocalized again?! I just don't understand this part of the code.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, the localization must be done where the class is used instead of the model.
As I introduced more annotation types like underline annotations or polygons, I ran out of symbols for the icon in the annotationList, thus, I have prepended the type of the marking to the title of the marking in the list, so that the user is aware what type of annotation it is.
However, in the actual content field this should not be there any more, as it is shown in the label in front of the TextArea.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, that sounds really complex.
Why don't you just pass the name of the type to the GUI and then add the translation there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because the DefaultListModel<FileAnnotation> listModel; uses the FileAnnotation.toString() method directly to render the JList<FileAnnotation> annotationList;. There we need the localized name of the AnnotationType, whereas in the private final JTextArea markedTxtArea; this would be a duplication, so I stripped it.
I'm not proficient in GUI programming, and this was the easiest solution I found.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OKay, then It's okay
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it the classical problem, that model objects are directly used in the gui to display information (and thus the model class gets modified in ways that are undesirable from a model view point, but needed in the gui). Here I would propose that:
FileAnnoationstores and returns the content as extracted from the PDF. It also provides way to determine its type (preferable as an enum)- You create a wrapper
FileAnnotationViewModelthat prepares the data to be displayed in the GUI (e.g. there is a method that converts the annotation type to a localized string, overwrites toString such that the content is displayed etc).
| Optional<File> expandedFileName = FileUtil.expandFilename(databaseContext, parsedFileField.getLink(), | ||
| JabRefPreferences.getInstance().getFileDirectoryPreferences()); | ||
| expandedFileName.ifPresent(file -> annotations.put(file.toString(), importer.importAnnotations(Paths.get(file.toString())))); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use file.toPath() directly
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed in 1dfa504
| for (PDAnnotation annotation : page.getAnnotations()) { | ||
| if (annotation.getSubtype().equals(FDFAnnotationHighlight.SUBTYPE)) { | ||
| annotationsList.add(createHighlightAnnotations(pageIndex, page, annotation)); | ||
| if (annotation.getSubtype().equals(FDFAnnotationHighlight.SUBTYPE) || |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should change the order of equals, the constant first and then the annotation.getsubType as it theoretically could ne null and would result in an NPE.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. bcf7516
| case FDFAnnotationHighlight.SUBTYPE: | ||
| label.setIcon(IconTheme.JabRefIcon.MARKER.getSmallIcon()); | ||
| break; | ||
| case FDFAnnotationUnderline.SUBTYPE: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is/Can FileAnnotation be made an enum?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I just checked it's strings..
|
|
||
| try { | ||
| annotation.setContents(extractHighlightedText(page, annotation)); | ||
| if (annotation.getSubtype().equals(FDFAnnotationHighlight.SUBTYPE) || annotation.getSubtype().equals(FDFAnnotationUnderline.SUBTYPE)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
same as above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes. bcf7516
Siedlerchr
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See comments
| // End old test | ||
|
|
||
| FileAnnotation expected = new FileAnnotation("Linus Dietz", LocalDateTime.of(2017, 3, 12, 20, 25), 1, "inline note annotation", "FreeText", Optional.empty()); | ||
| assertEquals("Comparison fails. WHY?", Collections.singletonList(expected), annotations); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not directly to see, but I did some debuggin and it fails because the FileAnnotation does not provide an equals method
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have added an equals method now
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah. That makes sense. Thanks a lot!
| assertEquals(1, note.getPage()); | ||
| // End old test style | ||
|
|
||
| assertEquals(expected, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks to @Siedlerchr for pointing out the necessity to override the .equals() method in FileAnnotation, however the reason for this was to actually improve the message which is displayed by jUnit if the test fails.
@tobiasdiez, can you give a hint why the message is better with the Collections.singletonList() variant? (see #2640 (comment))
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To see the value of comparison with lists, we need to assume that the method under test returns something unexpected. For example:
- It returns two items instead of one. With the "old style" you just get
expected 1 but got 2while comparison with the singleton list displays something likeExpected "item [property 1, ...]" but got "item [property 1, ....], item2 [value 1, ... ]". So you get more details about the item that is wrongly returned, which is often helpful in fixing bugs.
(for this you need to overwrite the toStrings method..I normally take the autogenerated variant generated by intellij) - The method still returns only one item but with the wrong values. In this case, the old style gives you
expected "value1" but got "wrong value". If you compare against an expected item you directly get also information about the other properties (thus you directly see if just one value is off or a completely different item is returned).
Another important advantage is that, if in the future the class is extended by an additional property, you just need to adapt the equals method and all your tests automatically take this new property in account.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Well, if I change any of the tests in the PdfAnnotationImporterTest such that one attribute of the single list element mismatches I don't get the rich comparison you are describing. Nevertheless I have implemented it as you have proposed.
tobiasdiez
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Code looks good to me, so I merge. Thanks for the refactoring / code improvement!
* upstream/master: Localization: General: French: Translation of new entries Localization: Menu: French: Translation of an entry (#2685) Fix #2680 and fix #2667: Swing errors are catched properly and without freezing (#2681) Do not log AND throw Replace misleading error message for fetcher connection error Document CrossRef test Fix subtitle detection for CrossRef fetcher Revert "Invoke LogMessages.add in JavaFX thread" Use global user agent Update mockito from 2.7.17 to 2.7.18 Move GuiAppender to GUI package Invoke LogMessages.add in JavaFX thread [WIP] Put the PDFAnnotationImporter under Test, enhance FileAnnotationTab (#2640) Fix for "Paying off technical debt: almost all utility classes have a private constructor now." (#2672) Revert "Paying off technical debt: almost all utility classes have a private constructor now. (#2649)" (#2670) Paying off technical debt: almost all utility classes have a private constructor now. (#2649) Changed codeformatting for better fxml annotation (#2668) Disalbe Google Scholar tests on all CI environments (#2654) Fix JSONException in Crossref fetcher as mentioned in #2442
I have created tests for the PDF importer. However, the coverage has yet to be improved and I'm planning to extend the FileAnnotationTab in this pull request. So this is the follow-up for #2624, but this time in TDD.
gradle localizationUpdate?