/* * Syncany, www.syncany.org * Copyright (C) 2011-2015 Philipp C. Heckel <philipp.heckel@gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.syncany.tests.integration.scenarios; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.syncany.tests.util.TestAssertUtil.assertFileListEquals; import org.junit.Ignore; import org.junit.Test; import org.syncany.plugins.transfer.TransferSettings; import org.syncany.tests.util.TestClient; import org.syncany.tests.util.TestConfigUtil; /* * This test class uses tests one scenario using the DatabaseVersionUpdateDetector. * The algorithm used is described in the respective class ('algorithm B'). * * //////////////////////////////////////////////////////////////////////////////////////////////////// * * TEST SCENARIO * ---------------------------------------------------------------------------------------------------- * T is the local timestamp, A/B/C are clients/machines, "-->" is a "sync up" operation, * "<--" is a "sync down" operation, "(CONFLICT X)" describes the comparison to be taken * and is described in detail below. * ---------------------------------------------------------------------------------------------------- * T A B C Remote Storage * ---------------------------------------------------------------------------------------------------- * * 01 ( , ,C1)/T=01 -------> db-c-1 * 02 ( , ,C2)/T=02 * 03 ( , ,C3)/T=03 -------> db-c-3 * 04 ( , ,C1)/T=01 < * 04 ( , ,C2)/T=02 < * 04 ( , ,C3)/T=03 <------------------------------ db-c-1, db-c-3 * 05 ( , ,C4)/T=05 -------> db-c-4 * 06 ( , ,C1)/T=01 < * 06 ( , ,C2)/T=02 < * 06 ( , ,C3)/T=03 < * 06 ( , ,C4)/T=05 <------------------------------------------------------ db-c-1, db-c-3, db-c-4 * 07 ( ,B1,C3)/T=07 ------------------------------> db-b-1 * 08 (A1, ,C4)/T=08 * 09 (A2, ,C4)/T=09 * 10 (A3, ,C4)/T=10 ------------------------------------------------------> db-a-3 * * (CONFLICT 1) <--------------------------------- db-c-4, db-a-3 * (A3, ,C4)/T=10 wins * (A3,B2,C4)/T=.. after merge * To be uploaded * * (CONFLICT 2) <--------------------------------------------------------- db-b-1 * (A3, ,C4)/T=10 wins * That's me. Do nothing. * * (CONFLICT 3) <---------- db-a-3, db-b-1 * (A3, ,C4)/T=10 wins * To be applied locally * * 11 (A4, ,C4)/T=11 * 12 (A5, ,C4)/T=12 ------------------------------------------------------> db-a-5 * 13 (A3, ,C5)/T=13 -------> db-c-5 * 14 (A3, ,C6)/T=14 * 15 (A3, ,C7)/T=15 -------> db-c-7 * 16 (A3,B2,C4)/T=16 * 17 (A3,B3,C4)/T=17 * 18 (A3,B4,C4)/T=18 ------------------------------> db-b-4 * 19 (A6, ,C4)/T=19 ------------------------------------------------------> db-a-6 * 20 (A3,B5,C4)/T=20 ------------------------------> db-b-5 * 21 (A3, ,C8)/T=21 -------> db-c-8 * * (CONFLICT 4) <--------------------------------- db-a-5, db-c-5, db-c-7, db-a-6, db-c-8 * (A6, ,C4)/T=19 wins * (A6,B6,C4)/T=.. after merge * To be uploaded * * (CONFLICT 5) <--------------------------------------------------------- db-c-5, db-c-7, db-b-4, db-b-5, db-c-8 * (A6, ,C4)/T=10 wins * That's me. Do nothing. * * (CONFLICT 6) <---------- db-a-5, db-b-4, db-a-6, db-b-5 * (A6, ,C4)/T=19 wins * (A6, ,C9)/T=.. after merge * To be uploaded * * 22 (A6, ,C9)/T=22 -------> db-c-9 * 23 (A6,B6,C4)/T=23 ------------------------------> db-b-6 * * (CONFLICT 7) <---------- db-b-1, db-b-4, db-b-5, db-b-6, db-b-9 * * (CONFLICT 8) <--------------------------------- db-c-9 * * (CONFLICT 9) <--------------------------------------------------------- db-b-6, db-c-9 * * //////////////////////////////////////////////////////////////////////////////////////////////////// * * CONFLICT 1 at B: * - Local: ( , ,C1)/T=01 * ( , ,C2)/T=02 * ( , ,C3)/T=03 * ( ,B1,C3)/T=07 (last B) * - db-c-4: ( , ,C1)/T=01 * ( , ,C2)/T=02 * ( , ,C3)/T=03 * ( , ,C4)/T=05 (last C) * - db-a-3: ( , ,C1)/T=01 * ( , ,C2)/T=02 * ( , ,C3)/T=03 * ( , ,C4)/T=05 * (A1, ,C4)/T=08 * (A2, ,C4)/T=09 * (A3, ,C4)/T=10 (last A) * * - Last versions conflicts: ( ,B1,C3)/T=07 || ( , ,C4)/T=05 * ( ,B1,C3)/T=07 || (A3, ,C4)/T=10 * - Last common version between clients: ( , ,C3)/T=03 * - First conflicting version per client: ( , ,C4)/T=05 (first conflicting A) * ( ,B1,C3)/T=07 (first conflicting B) * ( , ,C4)/T=05 (first conflicting C) * - Winner of first conflicting versions: ( , ,C4)/T=05 * - Winning clients histories: A C * ( , ,C4)/T=05 ( , ,C4)/T=05 * (A1, ,C4)/T=08 (empty) * (A2, ,C4)/T=09 * (A3, ,C4)/T=10 * - Conflicts in winning histories: (empty) * - Winning histories lengths A C * 4 1 * - Follow this history (ultimate winner): A * * - Result: * Winner: (A3, ,C4)/T=10 * --> Conflicts with local version * --> Local must merge local version ( ,B1,C3) in (A3, ,C4) * --> Local result is then (A3,B2,C4) * * CONFLICT 2 at A: * - Local: ( , ,C1)/T=01 * ( , ,C2)/T=02 * ( , ,C3)/T=03 * (A3, ,C4)/T=10 (last A) * - db-b-1: ( , ,C1)/T=01 * ( , ,C2)/T=02 * ( , ,C3)/T=03 * ( ,B1,C3)/T=07 (last B) * * - Last versions conflicts: ( ,B1,C3)/T=07 || (A3, ,C4)/T=10 * - Last common version between clients: ( , ,C3)/T=03 * - First conflicting version per client: ( , ,C4)/T=05 (first conflicting A) * ( ,B1,C3)/T=07 (first conflicting B) * - Winner of first conflicting versions: ( , ,C4)/T=05 * - Winning clients histories: A * ( , ,C4)/T=05 * (A1, ,C4)/T=08 * (A2, ,C4)/T=09 * (A3, ,C4)/T=10 * - Conflicts in winning histories: (empty) * - Winning histories lengths 4 * - Follow this history (ultimate winner): A * * - Result: * Winner: (A3, ,C4)/T=10 * --> That's me. Do nothing. * * CONFLICT 3 at C: * - Local: ( , ,C1)/T=01 * ( , ,C2)/T=02 * ( , ,C3)/T=03 * ( , ,C4)/T=05 (last C) * - db-b-1: ( , ,C1)/T=01 * ( , ,C2)/T=02 * ( , ,C3)/T=03 * ( ,B1,C3)/T=07 (last B) * - db-a-3: ( , ,C1)/T=01 * ( , ,C2)/T=02 * ( , ,C3)/T=03 * ( , ,C4)/T=05 * (A1, ,C4)/T=08 * (A2, ,C4)/T=09 * (A3, ,C4)/T=10 (last A) * * - Last versions conflicts: ( ,B1,C3)/T=07 || (A3, ,C4)/T=10 * - Last common version between clients: ( , ,C3)/T=03 * - First conflicting version per client: ( , ,C4)/T=05 (first conflicting A) * ( ,B1,C3)/T=07 (first conflicting B) * ( , ,C4)/T=05 (first conflicting C) * - Winner of first conflicting versions: ( , ,C4)/T=05 * - Winning clients histories: A C * ( , ,C4)/T=05 ( , ,C4)/T=05 * (A1, ,C4)/T=08 (empty) * (A2, ,C4)/T=09 * (A3, ,C4)/T=10 * - Conflicts in winning histories: (empty) * - Winning histories lengths A C * 4 1 * - Follow this history (ultimate winner): A * * - Result: * Winner: (A3, ,C4)/T=10 * --> That's not me. Must apply changes locally. * * CONFLICT 4 at B: * - Local: ( , ,C1)/T=01 * ( , ,C2)/T=02 * ( , ,C3)/T=03 * ( ,B1,C3)/T=07 * (A3,B2,C4)/T=16 (first conflict B) * (A3,B3,C4)/T=17 * (A3,B4,C4)/T=18 * (A3,B5,C4)/T=20 (last B) * - db-a-5: (A4, ,C4)/T=11 (first conflict A) * (A5, ,C4)/T=12 * - db-a-6: (A6, ,C4)/T=19 (last A) * - db-c-5: (A3, ,C5)/T=13 (first conflict C) * - db-c-7: (A3, ,C6)/T=14 * (A3, ,C7)/T=15 * - db-c-8: (A3, ,C8)/T=21 (last C) * * - Last versions conflicts: (A3,B5,C4)/T=20 || (A6, ,C4)/T=19 * (A3,B5,C4)/T=20 || (A3, ,C8)/T=21 * (A6, ,C4)/T=19 || (A3, ,C8)/T=21 * - Last common version between clients: (empty) * - First conflicting version per client: (A4, ,C4)/T=11 (first conflicting A) * (A3,B2,C4)/T=16 (first conflicting B) * (A3, ,C5)/T=13 (first conflicting C) * - Winner of first conflicting versions: (A4, ,C4)/T=11 * - Winning clients histories: A * (A4, ,C4)/T=11 * (A5, ,C4)/T=12 * (A6, ,C4)/T=19 * - Conflicts in winning histories: (empty) * - Winning histories lengths (not relevant) * - Follow this history (ultimate winner): A * * - Result: * Winner: (A6, ,C4)/T=19 * --> Conflicts with local version * --> Local must merge local version (A3,B5,C4) in (A6, ,C4) * --> Local result is then (A6,B6,C4) * * CONFLICT 5 at A: * - Local: ( , ,C1)/T=01 * ( , ,C2)/T=02 * ( , ,C3)/T=03 * ( , ,C4)/T=05 * (A4, ,C4)/T=11 (first conflict A) * (A5, ,C4)/T=12 * (A6, ,C4)/T=19 (last A) * - db-b-4: (A3,B2,C4)/T=16 (first conflict B) * (A3,B3,C4)/T=17 * (A3,B4,C4)/T=18 * - db-b-5: (A3,B5,C4)/T=20 (last B) * - db-c-5: (A3, ,C5)/T=13 (first conflict C) * - db-c-7: (A3, ,C6)/T=14 * (A3, ,C7)/T=15 * - db-c-8: (A3, ,C8)/T=21 (last C) * * - Last versions conflicts: (A3,B5,C4)/T=20 || (A6, ,C4)/T=19 * (A3,B5,C4)/T=20 || (A3, ,C8)/T=21 * (A6, ,C4)/T=19 || (A3, ,C8)/T=21 * - Last common version between clients: (empty) * - First conflicting version per client: (A4, ,C4)/T=11 (first conflicting A) * (A3,B2,C4)/T=16 (first conflicting B) * (A3, ,C5)/T=13 (first conflicting C) * - Winner of first conflicting versions: (A4, ,C4)/T=11 * - Winning clients histories: A * (A4, ,C4)/T=11 * (A5, ,C4)/T=12 * (A6, ,C4)/T=19 * - Conflicts in winning histories: (empty) * - Winning histories lengths (not relevant) * - Follow this history (ultimate winner): A * * - Result: * Winner: (A6, ,C4)/T=19 * --> That's me. Do nothing. * * CONFLICT 6 at C: * - Local: ( , ,C1)/T=01 * ( , ,C2)/T=02 * ( , ,C3)/T=03 * ( , ,C4)/T=05 * (A3, ,C5)/T=13 (first conflict C) * (A3, ,C6)/T=14 * (A3, ,C7)/T=15 * (A3, ,C8)/T=21 (last C) * - db-a-5: (A4, ,C4)/T=11 (first conflict A) * (A5, ,C4)/T=12 * - db-a-6: (A6, ,C4)/T=19 (last A) * - db-b-4: (A3,B2,C4)/T=16 (first conflict B) * (A3,B3,C4)/T=17 * (A3,B4,C4)/T=18 * - db-b-5: (A3,B5,C4)/T=20 (last B) * * - Last versions conflicts: (A3,B5,C4)/T=20 || (A6, ,C4)/T=19 * (A3,B5,C4)/T=20 || (A3, ,C8)/T=21 * (A6, ,C4)/T=19 || (A3, ,C8)/T=21 * - Last common version between clients: (empty) * - First conflicting version per client: (A4, ,C4)/T=11 (first conflicting A) * (A3,B2,C4)/T=16 (first conflicting B) * (A3, ,C5)/T=13 (first conflicting C) * - Winner of first conflicting versions: (A4, ,C4)/T=11 * - Winning clients histories: A * (A4, ,C4)/T=11 * (A5, ,C4)/T=12 * (A6, ,C4)/T=19 * - Conflicts in winning histories: (empty) * - Winning histories lengths (not relevant) * - Follow this history (ultimate winner): A * * - Result: * Winner: (A6, ,C4)/T=19 * --> Conflicts with local version * --> Local must merge local version (A3, ,C8) in (A6, ,C4) * --> Local result is then (A6, ,C9) * * CONFLICT 7 at C: * - Local: ( , ,C1)/T=01 * ( , ,C2)/T=02 * ( , ,C3)/T=03 * ( , ,C4)/T=05 * (A3, ,C5)/T=13 * (A3, ,C6)/T=14 * (A3, ,C7)/T=15 * (A3, ,C8)/T=21 * (A3, ,C9)/T=22 (last C) * - db-b-1: ( , ,C1)/T=01 * ( , ,C2)/T=02 * ( , ,C3)/T=03 * ( ,B1,C3)/T=07 * - db-b-4: (A3,B2,C4)/T=16 (first conflict B) * (A3,B3,C4)/T=17 * (A3,B4,C4)/T=18 * - db-b-5: (A3,B5,C4)/T=20 * - db-b-6: (A6,B6,C4)/T=23 (last B) * * * * * * * - Last versions conflicts: (A6,B5,C4)/T=23 || (A3, ,C9)/T=22 * - Last common version between clients: (empty) * - First conflicting version per client: (A3, ,C9)/T=22 (first conflicting C) * (A6,B6,C4)/T=23 (first conflicting B) * - Winner of first conflicting versions: (A4, ,C4)/T=11 * - Winning clients histories: A * (A4, ,C4)/T=11 * (A5, ,C4)/T=12 * (A6, ,C4)/T=19 * - Conflicts in winning histories: (empty) * - Winning histories lengths (not relevant) * - Follow this history (ultimate winner): A * * - Result: * Winner: (A6, ,C4)/T=19 * --> Conflicts with local version * --> Local must merge local version (A3, ,C8) in (A6, ,C4) * --> Local result is then (A6, ,C9) */ public class MixedUpDownScenarioTest { @Test @Ignore public void testMixedScenario1() throws Exception { // Setup TransferSettings testConnection = TestConfigUtil.createTestLocalConnection(); TestClient clientA = new TestClient("A", testConnection); TestClient clientB = new TestClient("B", testConnection); TestClient clientC = new TestClient("C", testConnection); // Run clientC.createNewFile("C1"); clientC.up(); clientC.createNewFile("C2"); clientC.up(); clientC.createNewFile("C3"); clientC.up(); clientB.down(); // NO CONFLICT assertFileListEquals("Client B and C should be on the same versions.", clientB.getLocalFilesExcludeLockedAndNoRead(), clientC.getLocalFilesExcludeLockedAndNoRead()); clientC.createNewFile("C4"); // up without down! Evil! clientC.up(); clientA.down(); // NO CONFLICT assertFileListEquals("Client A and C should be on the same versions.", clientA.getLocalFilesExcludeLockedAndNoRead(), clientC.getLocalFilesExcludeLockedAndNoRead()); assertEquals("Client A should have C4, client B should not", clientA.getLocalFilesExcludeLockedAndNoRead().size()-1, clientB.getLocalFilesExcludeLockedAndNoRead().size()); clientB.createNewFile("B1,C3"); clientB.up(); clientA.createNewFile("A1,C4"); clientA.up(); clientA.createNewFile("A2,C4"); clientA.up(); clientA.createNewFile("A3,C4"); clientA.up(); clientB.down(); // CONFLICT 1 fail("Add some asserts"); assertFileListEquals(clientA.getLocalFilesExcludeLockedAndNoRead(), clientB.getLocalFilesExcludeLockedAndNoRead()); clientA.down(); // CONFLICT 2 clientC.down(); // CONFLICT 3 assertFileListEquals(clientA.getLocalFilesExcludeLockedAndNoRead(), clientB.getLocalFilesExcludeLockedAndNoRead()); assertFileListEquals(clientB.getLocalFilesExcludeLockedAndNoRead(), clientC.getLocalFilesExcludeLockedAndNoRead()); clientA.createNewFile("A4,C4"); clientA.up(); clientA.createNewFile("A5,C4"); clientA.up(); clientC.createNewFile("A3,C5"); clientC.up(); clientC.createNewFile("A3,C6"); clientC.up(); clientC.createNewFile("A3,C7"); clientC.up(); clientB.createNewFile("A3,B2,C4"); clientB.up(); clientB.createNewFile("A3,B3,C4"); clientB.up(); clientB.createNewFile("A3,B4,C4"); clientB.up(); clientA.createNewFile("A6,C4"); clientA.up(); clientB.createNewFile("A3,B5,C4"); clientB.up(); clientC.createNewFile("A3,C8"); clientC.up(); clientB.down(); // CONFLICT 4 clientA.down(); // CONFLICT 5 clientC.down(); // CONFLICT 6 clientA.up(); clientA.down(); clientB.up(); clientB.down(); clientC.up(); clientC.down(); clientC.up(); clientB.up(); clientC.down(); // CONFLICT 7 clientB.down(); // CONFLICT 8 clientA.down(); // CONFLICT 9 assertFileListEquals(clientA.getLocalFilesExcludeLockedAndNoRead(), clientB.getLocalFilesExcludeLockedAndNoRead()); assertFileListEquals(clientB.getLocalFilesExcludeLockedAndNoRead(), clientC.getLocalFilesExcludeLockedAndNoRead()); assertEquals("File list count does not match.", 19, clientA.getLocalFilesExcludeLockedAndNoRead().size()); // Tear down clientA.deleteTestData(); clientB.deleteTestData(); clientC.deleteTestData(); } }