package org.jabref.shared; import java.sql.SQLException; import java.util.Collection; import org.jabref.model.Defaults; import org.jabref.model.bibtexkeypattern.GlobalBibtexKeyPattern; import org.jabref.model.database.BibDatabaseContext; import org.jabref.model.database.BibDatabaseMode; import org.jabref.model.database.DatabaseLocation; import org.jabref.model.entry.BibEntry; import org.jabref.shared.exception.DatabaseNotSupportedException; import org.jabref.shared.exception.InvalidDBMSConnectionPropertiesException; import org.jabref.testutils.category.DatabaseTests; import org.junit.After; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import org.junit.runners.Parameterized.Parameter; import org.junit.runners.Parameterized.Parameters; @RunWith(Parameterized.class) @Category(DatabaseTests.class) public class SynchronizationTestSimulator { private BibDatabaseContext clientContextA; private BibDatabaseContext clientContextB; private SynchronizationTestEventListener eventListenerB; // used to monitor occurring events private DBMSConnection dbmsConnection; @Parameter public DBMSType dbmsType; @Before public void setUp() throws SQLException, DatabaseNotSupportedException, InvalidDBMSConnectionPropertiesException { this.dbmsConnection = TestConnector.getTestDBMSConnection(dbmsType); GlobalBibtexKeyPattern pattern = GlobalBibtexKeyPattern.fromPattern("[auth][year]"); clientContextA = new BibDatabaseContext(new Defaults(BibDatabaseMode.BIBTEX), DatabaseLocation.SHARED, ',', pattern); clientContextA.getDBMSSynchronizer().openSharedDatabase(dbmsConnection); clientContextB = new BibDatabaseContext(new Defaults(BibDatabaseMode.BIBTEX), DatabaseLocation.SHARED, ',', pattern); clientContextB.getDBMSSynchronizer().openSharedDatabase(dbmsConnection); eventListenerB = new SynchronizationTestEventListener(); clientContextB.getDBMSSynchronizer().registerListener(eventListenerB); } @Parameters(name = "Test with {0} database system") public static Collection<DBMSType> getTestingDatabaseSystems() { return TestManager.getDBMSTypeTestParameter(); } @Test public void simulateEntryInsertionAndManualPull() { clientContextA.getDatabase().insertEntry(getBibEntryExample(1)); // client A inserts an entry clientContextA.getDatabase().insertEntry(getBibEntryExample(2)); // client A inserts another entry clientContextB.getDBMSSynchronizer().pullChanges(); // client B pulls the changes Assert.assertEquals(clientContextA.getDatabase().getEntries(), clientContextB.getDatabase().getEntries()); } @Test public void simulateEntryUpdateAndManualPull() { BibEntry bibEntry = getBibEntryExample(1); clientContextA.getDatabase().insertEntry(bibEntry); // client A inserts an entry bibEntry.setField("custom", "custom value"); // client A changes the entry bibEntry.clearField("author"); clientContextB.getDBMSSynchronizer().pullChanges(); // client B pulls the changes Assert.assertEquals(clientContextA.getDatabase().getEntries(), clientContextB.getDatabase().getEntries()); } @Test public void simulateEntryDelitionAndManualPull() { BibEntry bibEntry = getBibEntryExample(1); clientContextA.getDatabase().insertEntry(bibEntry); // client A inserts an entry clientContextB.getDBMSSynchronizer().pullChanges(); // client B pulls the entry Assert.assertFalse(clientContextA.getDatabase().getEntries().isEmpty()); Assert.assertFalse(clientContextB.getDatabase().getEntries().isEmpty()); Assert.assertEquals(clientContextA.getDatabase().getEntries(), clientContextB.getDatabase().getEntries()); clientContextA.getDatabase().removeEntry(bibEntry); // client A removes the entry clientContextB.getDBMSSynchronizer().pullChanges(); // client B pulls the change Assert.assertTrue(clientContextA.getDatabase().getEntries().isEmpty()); Assert.assertTrue(clientContextB.getDatabase().getEntries().isEmpty()); } @Test public void simulateUpdateOnNoLongerExistingEntry() { BibEntry bibEntryOfClientA = getBibEntryExample(1); clientContextA.getDatabase().insertEntry(bibEntryOfClientA); // client A inserts an entry clientContextB.getDBMSSynchronizer().pullChanges(); // client B pulls the entry Assert.assertFalse(clientContextA.getDatabase().getEntries().isEmpty()); Assert.assertFalse(clientContextB.getDatabase().getEntries().isEmpty()); Assert.assertEquals(clientContextA.getDatabase().getEntries(), clientContextB.getDatabase().getEntries()); clientContextA.getDatabase().removeEntry(bibEntryOfClientA); // client A removes the entry Assert.assertFalse(clientContextB.getDatabase().getEntries().isEmpty()); Assert.assertNull(eventListenerB.getSharedEntryNotPresentEvent()); BibEntry bibEntryOfClientB = clientContextB.getDatabase().getEntries().get(0); // client B tries to update the entry bibEntryOfClientB.setField("year", "2009"); // here a new SharedEntryNotPresentEvent has been thrown. In this case the user B would get an pop-up window. Assert.assertNotNull(eventListenerB.getSharedEntryNotPresentEvent()); Assert.assertEquals(bibEntryOfClientB, eventListenerB.getSharedEntryNotPresentEvent().getBibEntry()); } @Test public void simulateEntryChangeConflicts() { BibEntry bibEntryOfClientA = getBibEntryExample(1); clientContextA.getDatabase().insertEntry(bibEntryOfClientA); // client A inserts an entry clientContextB.getDBMSSynchronizer().pullChanges(); // client B pulls the entry bibEntryOfClientA.setField("year", "2001"); // A now increases the version number // B does nothing here, so there is no event occurrence // B now tries to update the entry Assert.assertFalse(clientContextB.getDatabase().getEntries().isEmpty()); Assert.assertNull(eventListenerB.getUpdateRefusedEvent()); BibEntry bibEntryOfClientB = clientContextB.getDatabase().getEntries().get(0); bibEntryOfClientB.setField("year", "2016"); // B also tries to change something // B now cannot update the shared entry, due to optimistic offline lock. // In this case an BibEntry merge dialog pops up. Assert.assertNotNull(eventListenerB.getUpdateRefusedEvent()); } private BibEntry getBibEntryExample(int index) { BibEntry bibEntry = new BibEntry(); bibEntry.setType("inproceedings"); bibEntry.setField("author", "Wirthlin, Michael J and Hutchings, Brad L and Gilson, Kent L " + index); bibEntry.setField("title", "The nano processor: a low resource reconfigurable processor " + index); bibEntry.setField("booktitle", "FPGAs for Custom Computing Machines, 1994. Proceedings. IEEE Workshop on " + index); bibEntry.setField("year", "199" + index); bibEntry.setCiteKey("nanoproc199" + index); return bibEntry; } @After public void clear() throws SQLException { TestManager.clearTables(dbmsConnection); } }