package org.jabref.logic.integrity;
import java.io.File;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.jabref.logic.bibtexkeypattern.BibtexKeyPatternPreferences;
import org.jabref.logic.journals.Abbreviation;
import org.jabref.logic.journals.JournalAbbreviationRepository;
import org.jabref.model.Defaults;
import org.jabref.model.bibtexkeypattern.GlobalBibtexKeyPattern;
import org.jabref.model.database.BibDatabase;
import org.jabref.model.database.BibDatabaseContext;
import org.jabref.model.database.BibDatabaseMode;
import org.jabref.model.entry.BibEntry;
import org.jabref.model.entry.InternalBibtexFields;
import org.jabref.model.metadata.FileDirectoryPreferences;
import org.jabref.model.metadata.MetaData;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import org.mockito.Mockito;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
public class IntegrityCheckTest {
@Rule
public TemporaryFolder testFolder = new TemporaryFolder();
@Test
public void testEntryTypeChecks() {
assertCorrect(withMode(createContext("title", "sometitle", "article"), BibDatabaseMode.BIBTEX));
assertWrong(withMode(createContext("title", "sometitle", "patent"), BibDatabaseMode.BIBTEX));
assertCorrect((withMode(createContext("title", "sometitle", "patent"), BibDatabaseMode.BIBLATEX)));
assertCorrect(withMode(createContext("title", "sometitle", "article"), BibDatabaseMode.BIBLATEX));
}
@Test
public void testUrlChecks() {
assertCorrect(createContext("url", "http://www.google.com"));
assertCorrect(createContext("url", "file://c:/asdf/asdf"));
assertCorrect(createContext("url", "http://scikit-learn.org/stable/modules/ensemble.html#random-forests"));
assertWrong(createContext("url", "www.google.com"));
assertWrong(createContext("url", "google.com"));
assertWrong(createContext("url", "c:/asdf/asdf"));
}
@Test
public void testYearChecks() {
assertCorrect(createContext("year", "2014"));
assertCorrect(createContext("year", "1986"));
assertCorrect(createContext("year", "around 1986"));
assertCorrect(createContext("year", "(around 1986)"));
assertCorrect(createContext("year", "1986,"));
assertCorrect(createContext("year", "1986}%"));
assertCorrect(createContext("year", "1986(){},.;!?<>%&$"));
assertWrong(createContext("year", "abc"));
assertWrong(createContext("year", "86"));
assertWrong(createContext("year", "204"));
assertWrong(createContext("year", "1986a"));
assertWrong(createContext("year", "(1986a)"));
assertWrong(createContext("year", "1986a,"));
assertWrong(createContext("year", "1986}a%"));
assertWrong(createContext("year", "1986a(){},.;!?<>%&$"));
}
@Test
public void testEditionChecks() {
assertCorrect(withMode(createContext("edition", "Second"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("edition", "Third"), BibDatabaseMode.BIBTEX));
assertWrong(withMode(createContext("edition", "second"), BibDatabaseMode.BIBTEX));
assertWrong(withMode(createContext("edition", "2"), BibDatabaseMode.BIBTEX));
assertWrong(withMode(createContext("edition", "2nd"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("edition", "2"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("edition", "10"), BibDatabaseMode.BIBLATEX));
assertCorrect(
withMode(createContext("edition", "Third, revised and expanded edition"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("edition", "Edition 2000"), BibDatabaseMode.BIBLATEX));
assertWrong(withMode(createContext("edition", "2nd"), BibDatabaseMode.BIBLATEX));
}
@Test
public void testNoteChecks() {
assertCorrect(withMode(createContext("note", "Lorem ipsum"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("note", "Lorem ipsum? 10"), BibDatabaseMode.BIBTEX));
assertWrong(withMode(createContext("note", "lorem ipsum"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("note", "Lorem ipsum"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("note", "\\url{someurl}"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("note", "lorem ipsum"), BibDatabaseMode.BIBLATEX));
}
@Test
public void testHowpublishedChecks() {
assertCorrect(withMode(createContext("howpublished", "Lorem ipsum"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("howpublished", "Lorem ipsum? 10"), BibDatabaseMode.BIBTEX));
assertWrong(withMode(createContext("howpublished", "lorem ipsum"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("howpublished", "\\url{someurl}"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("howpublished", "Lorem ipsum"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("howpublished", "lorem ipsum"), BibDatabaseMode.BIBLATEX));
}
@Test
public void testMonthChecks() {
assertCorrect(withMode(createContext("month", "#mar#"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("month", "#dec#"), BibDatabaseMode.BIBTEX));
assertWrong(withMode(createContext("month", "#bla#"), BibDatabaseMode.BIBTEX));
assertWrong(withMode(createContext("month", "Dec"), BibDatabaseMode.BIBTEX));
assertWrong(withMode(createContext("month", "December"), BibDatabaseMode.BIBTEX));
assertWrong(withMode(createContext("month", "Lorem"), BibDatabaseMode.BIBTEX));
assertWrong(withMode(createContext("month", "10"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("month", "1"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("month", "10"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("month", "#jan#"), BibDatabaseMode.BIBLATEX));
assertWrong(withMode(createContext("month", "jan"), BibDatabaseMode.BIBLATEX));
assertWrong(withMode(createContext("month", "january"), BibDatabaseMode.BIBLATEX));
assertWrong(withMode(createContext("month", "January"), BibDatabaseMode.BIBLATEX));
assertWrong(withMode(createContext("month", "Lorem"), BibDatabaseMode.BIBLATEX));
}
@Test
public void testJournaltitleChecks() {
assertWrong(withMode(createContext("journaltitle", "A journal"), BibDatabaseMode.BIBLATEX));
assertWrong(withMode(createContext("journaltitle", "A journal"), BibDatabaseMode.BIBTEX));
}
@Test
public void testBibtexkeyChecks() {
final BibDatabaseContext correctContext = createContext("bibtexkey", "Knuth2014");
correctContext.getDatabase().getEntries().get(0).setField("author","Knuth");
correctContext.getDatabase().getEntries().get(0).setField("year","2014");
assertCorrect(correctContext);
final BibDatabaseContext wrongContext = createContext("bibtexkey", "Knuth2014a");
wrongContext.getDatabase().getEntries().get(0).setField("author","Knuth");
wrongContext.getDatabase().getEntries().get(0).setField("year","2014");
assertWrong(wrongContext);
}
@Test
public void testBracketChecks() {
assertCorrect(createContext("title", "x"));
assertCorrect(createContext("title", "{x}"));
assertCorrect(createContext("title", "{x}x{}x{{}}"));
assertWrong(createContext("title", "{x}x{}}x{{}}"));
assertWrong(createContext("title", "}"));
assertWrong(createContext("title", "{"));
}
@Test
public void testAuthorNameChecks() {
for (String field : InternalBibtexFields.getPersonNameFields()) {
// getPersonNameFields returns fields that are available in biblatex only
// if run without mode, the NoBibtexFieldChecker will complain that "afterword" is a biblatex only field
assertCorrect(withMode(createContext(field, ""), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext(field, "Knuth"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext(field, " Knuth, Donald E. "), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext(field, "Knuth, Donald E. and Kurt Cobain and A. Einstein"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext(field, "Donald E. Knuth and Kurt Cobain and A. Einstein"), BibDatabaseMode.BIBLATEX));
assertWrong(withMode(createContext(field, ", and Kurt Cobain and A. Einstein"), BibDatabaseMode.BIBLATEX));
assertWrong(withMode(createContext(field, "Donald E. Knuth and Kurt Cobain and ,"), BibDatabaseMode.BIBLATEX));
assertWrong(withMode(createContext(field, "and Kurt Cobain and A. Einstein"), BibDatabaseMode.BIBLATEX));
assertWrong(withMode(createContext(field, "Donald E. Knuth and Kurt Cobain and"), BibDatabaseMode.BIBLATEX));
}
}
@Test
public void testTitleChecks() {
assertCorrect(withMode(createContext("title", "This is a title"), BibDatabaseMode.BIBTEX));
assertWrong(withMode(createContext("title", "This is a Title"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("title", "This is a {T}itle"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("title", "{This is a Title}"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("title", "This is a {Title}"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("title", "{C}urrent {C}hronicle"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("title", "{A Model-Driven Approach for Monitoring {ebBP} BusinessTransactions}"), BibDatabaseMode.BIBTEX));
assertCorrect(withMode(createContext("title", "This is a title"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("title", "This is a Title"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("title", "This is a {T}itle"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("title", "{This is a Title}"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("title", "This is a {Title}"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("title", "{C}urrent {C}hronicle"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("title", "{A Model-Driven Approach for Monitoring {ebBP} BusinessTransactions}"), BibDatabaseMode.BIBLATEX));
}
@Test
public void testAbbreviationChecks() {
for (String field : Arrays.asList("booktitle", "journal")) {
assertCorrect(createContext(field, "IEEE SW"));
assertWrong(createContext(field, "Proc. of the"));
}
}
@Test
public void testJournalIsKnownInAbbreviationList() {
assertCorrect(createContext("journal", "IEEE Software"));
assertWrong(createContext("journal", "IEEE Whocares"));
}
@Test
public void testFileChecks() {
MetaData metaData = mock(MetaData.class);
Mockito.when(metaData.getDefaultFileDirectory()).thenReturn(Optional.of("."));
Mockito.when(metaData.getUserFileDirectory(any(String.class))).thenReturn(Optional.empty());
// FIXME: must be set as checkBibtexDatabase only activates title checker based on database mode
Mockito.when(metaData.getMode()).thenReturn(Optional.of(BibDatabaseMode.BIBTEX));
assertCorrect(createContext("file", ":build.gradle:gradle", metaData));
assertCorrect(createContext("file", "description:build.gradle:gradle", metaData));
assertWrong(createContext("file", ":asflakjfwofja:PDF", metaData));
}
@Test
public void fileCheckFindsFilesRelativeToBibFile() throws IOException {
File bibFile = testFolder.newFile("lit.bib");
testFolder.newFile("file.pdf");
BibDatabaseContext databaseContext = createContext("file", ":file.pdf:PDF");
databaseContext.setDatabaseFile(bibFile);
assertCorrect(databaseContext);
}
@Test
public void testTypeChecks() {
assertCorrect(createContext("pages", "11--15", "inproceedings"));
assertWrong(createContext("pages", "11--15", "proceedings"));
}
@Test
public void testBooktitleChecks() {
assertCorrect(createContext("booktitle", "2014 Fourth International Conference on Digital Information and Communication Technology and it's Applications (DICTAP)", "proceedings"));
assertWrong(createContext("booktitle", "Digital Information and Communication Technology and it's Applications (DICTAP), 2014 Fourth International Conference on", "proceedings"));
}
@Test
public void testPageNumbersChecks() {
assertCorrect(createContext("pages", "1--2"));
assertCorrect(createContext("pages", "12"));
assertWrong(createContext("pages", "1-2"));
assertCorrect(createContext("pages", "1,2,3"));
assertCorrect(createContext("pages", "43+"));
assertWrong(createContext("pages", "1 2"));
assertWrong(createContext("pages", "{1}-{2}"));
assertCorrect(createContext("pages", "7,41,73--97"));
assertCorrect(createContext("pages", "7,41--42,73"));
assertCorrect(createContext("pages", "7--11,41--43,73"));
assertCorrect(createContext("pages", "7+,41--43,73"));
}
@Test
public void testBiblatexPageNumbersChecks() {
assertCorrect(withMode(createContext("pages", "1--2"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("pages", "12"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("pages", "1-2"), BibDatabaseMode.BIBLATEX)); // only diff to bibtex
assertCorrect(withMode(createContext("pages", "1,2,3"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("pages", "43+"), BibDatabaseMode.BIBLATEX));
assertWrong(withMode(createContext("pages", "1 2"), BibDatabaseMode.BIBLATEX));
assertWrong(withMode(createContext("pages", "{1}-{2}"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("pages", "7,41,73--97"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("pages", "7,41--42,73"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("pages", "7--11,41--43,73"), BibDatabaseMode.BIBLATEX));
assertCorrect(withMode(createContext("pages", "7+,41--43,73"), BibDatabaseMode.BIBLATEX));
}
@Test
public void testBibStringChecks() {
assertCorrect(createContext("title", "Not a single hash mark"));
assertCorrect(createContext("month", "#jan#"));
assertCorrect(createContext("author", "#einstein# and #newton#"));
assertWrong(createContext("month", "#jan"));
assertWrong(createContext("author", "#einstein# #amp; #newton#"));
}
@Test
public void testHTMLCharacterChecks() {
assertCorrect(createContext("title", "Not a single {HTML} character"));
assertCorrect(createContext("month", "#jan#"));
assertCorrect(createContext("author", "A. Einstein and I. Newton"));
assertCorrect(createContext("url", "http://www.thinkmind.org/index.php?view=article&articleid=cloud_computing_2013_1_20_20130"));
assertWrong(createContext("author", "Lenhard, Jörg"));
assertWrong(createContext("author", "Lenhard, Jãrg"));
assertWrong(createContext("journal", "Ärling Ström for – ‱"));
}
@Test
public void testISSNChecks() {
assertCorrect(createContext("issn", "0020-7217"));
assertCorrect(createContext("issn", "1687-6180"));
assertCorrect(createContext("issn", "2434-561x"));
assertWrong(createContext("issn", "Some other stuff"));
assertWrong(createContext("issn", "0020-7218"));
}
@Test
public void testISBNChecks() {
assertCorrect(createContext("isbn", "0-201-53082-1"));
assertCorrect(createContext("isbn", "0-9752298-0-X"));
assertCorrect(createContext("isbn", "978-0-306-40615-7"));
assertWrong(createContext("isbn", "Some other stuff"));
assertWrong(createContext("isbn", "0-201-53082-2"));
assertWrong(createContext("isbn", "978-0-306-40615-8"));
}
@Test
public void testDOIChecks() {
assertCorrect(createContext("doi", "10.1023/A:1022883727209"));
assertCorrect(createContext("doi", "10.17487/rfc1436"));
assertCorrect(createContext("doi", "10.1002/(SICI)1097-4571(199205)43:4<284::AID-ASI3>3.0.CO;2-0"));
assertWrong(createContext("doi", "asdf"));
}
@Test
public void testEntryIsUnchangedAfterChecks() {
BibEntry entry = new BibEntry();
// populate with all known fields
for (String fieldName : InternalBibtexFields.getAllPublicAndInternalFieldNames()) {
entry.setField(fieldName, UUID.randomUUID().toString());
}
// add a random field
entry.setField(UUID.randomUUID().toString(), UUID.randomUUID().toString());
// duplicate entry
BibEntry clonedEntry = (BibEntry) entry.clone();
BibDatabase bibDatabase = new BibDatabase();
bibDatabase.insertEntry(entry);
BibDatabaseContext context = new BibDatabaseContext(bibDatabase, new Defaults());
new IntegrityCheck(context,
mock(FileDirectoryPreferences.class),
createBibtexKeyPatternPreferences(),
new JournalAbbreviationRepository(new Abbreviation("IEEE Software", "IEEE SW")))
.checkBibtexDatabase();
assertEquals(clonedEntry, entry);
}
@Test
public void testASCIIChecks() {
assertCorrect(createContext("title", "Only ascii characters!'@12"));
assertWrong(createContext("month", "Umlauts are nöt ällowed"));
assertWrong(createContext("author", "Some unicode ⊕"));
}
private BibDatabaseContext createContext(String field, String value, String type) {
BibEntry entry = new BibEntry();
entry.setField(field, value);
entry.setType(type);
BibDatabase bibDatabase = new BibDatabase();
bibDatabase.insertEntry(entry);
return new BibDatabaseContext(bibDatabase, new Defaults());
}
private BibDatabaseContext createContext(String field, String value, MetaData metaData) {
BibEntry entry = new BibEntry();
entry.setField(field, value);
BibDatabase bibDatabase = new BibDatabase();
bibDatabase.insertEntry(entry);
return new BibDatabaseContext(bibDatabase, metaData, new Defaults());
}
private BibDatabaseContext createContext(String field, String value) {
return createContext(field, value, new MetaData());
}
private void assertWrong(BibDatabaseContext context) {
List<IntegrityMessage> messages = new IntegrityCheck(context,
mock(FileDirectoryPreferences.class),
createBibtexKeyPatternPreferences(),
new JournalAbbreviationRepository(new Abbreviation("IEEE Software", "IEEE SW")))
.checkBibtexDatabase();
assertFalse(messages.toString(), messages.isEmpty());
}
private void assertCorrect(BibDatabaseContext context) {
List<IntegrityMessage> messages = new IntegrityCheck(context,
mock(FileDirectoryPreferences.class),
createBibtexKeyPatternPreferences(),
new JournalAbbreviationRepository(new Abbreviation("IEEE Software", "IEEE SW"))
).checkBibtexDatabase();
assertEquals(Collections.emptyList(), messages);
}
private BibtexKeyPatternPreferences createBibtexKeyPatternPreferences() {
final GlobalBibtexKeyPattern keyPattern = GlobalBibtexKeyPattern.fromPattern("[auth][year]");
return new BibtexKeyPatternPreferences(
"",
"",
false,
false,
false,
keyPattern,
',');
}
private BibDatabaseContext withMode(BibDatabaseContext context, BibDatabaseMode mode) {
context.setMode(mode);
return context;
}
}