[WIP] Put the PDFAnnotationImporter under Test, enhance FileAnnotationTab#2640
Conversation
* Fixed localization Issues
…Annotation and EntryAnnotationImporterTest
| 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.
maybe change to Logger.debug?
| 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.
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.
| @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.
As above, check versus an expected list:
FileAnnotation expected = new ..... and assertEquals(Collections.singletonList(expected), annotations))
There was a problem hiding this comment.
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.
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.
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.
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.
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.
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.
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.
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())))); | ||
| } |
| 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.
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.
| case FDFAnnotationHighlight.SUBTYPE: | ||
| label.setIcon(IconTheme.JabRefIcon.MARKER.getSmallIcon()); | ||
| break; | ||
| case FDFAnnotationUnderline.SUBTYPE: |
There was a problem hiding this comment.
Is/Can FileAnnotation be made an enum?
There was a problem hiding this comment.
Ah I just checked it's strings..
|
|
||
| try { | ||
| annotation.setContents(extractHighlightedText(page, annotation)); | ||
| if (annotation.getSubtype().equals(FDFAnnotationHighlight.SUBTYPE) || annotation.getSubtype().equals(FDFAnnotationUnderline.SUBTYPE)) { |
| // 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.
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.
I have added an equals method now
There was a problem hiding this comment.
Ah. That makes sense. Thanks a lot!
| assertEquals(1, note.getPage()); | ||
| // End old test style | ||
|
|
||
| assertEquals(expected, |
There was a problem hiding this comment.
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.
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.
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.
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?