package org.syncany.tests.integration.scenarios; import org.junit.Test; import org.syncany.operations.up.UpOperationOptions; import org.syncany.plugins.transfer.files.DatabaseRemoteFile; import org.syncany.plugins.unreliable_local.UnreliableLocalTransferSettings; import org.syncany.tests.util.TestClient; import org.syncany.tests.util.TestConfigUtil; import java.io.File; import java.util.Arrays; import static junit.framework.Assert.assertEquals; import static org.junit.Assert.assertTrue; /** * This test is designed for the scenario where a sync up consisting of multiple transactions fails during the * second mulitchunk of the first transaction. Expected outcome is that transaction and database files are * written to the state folder for all remaining transactions, and that the first transaction is resumed from * the second multichunk onwards. * * @author Tim Hegeman */ public class FailedSplitSyncUpScenarioTest { @Test public void testUpFailsOnFirstTransaction() throws Exception { // Inject failure for the second multichunk UnreliableLocalTransferSettings testConnection = TestConfigUtil.createTestUnreliableLocalConnection( Arrays.asList("rel=[4567].+upload.+multichunk")); TestClient clientA = new TestClient("A", testConnection); UpOperationOptions options = new UpOperationOptions(); options.setTransactionSizeLimit(0L); // Write three files (three transactions), with the first file spanning two multichunks clientA.createNewFile("file1", 5 * 1024 * 1024); clientA.createNewFile("file2", 1024); clientA.createNewFile("file3", 1024); // 1. Attempt upload, should fail boolean operationFailed = false; try { clientA.up(options); } catch (Exception ex) { operationFailed = true; } assertTrue(operationFailed); // 2. Verify local state File stateDir = clientA.getConfig().getStateDir(); File cacheDir = clientA.getConfig().getCacheDir(); // Expecting: 3 transactions + 3 databases + transaction list + in-progress transaction assertEquals(8, stateDir.listFiles().length); // Expecting: 3 databases + 4 multichunks + in-progress transaction assertEquals(8, cacheDir.listFiles().length); // 3. Verify remote state File repoActionsDir = new File(testConnection.getPath() + "/actions"); File repoDatabasesDir = new File(testConnection.getPath() + "/databases"); File repoMultichunksDir = new File(testConnection.getPath() + "/multichunks"); File repoTemporaryDir = new File(testConnection.getPath() + "/temporary"); File repoTransactionsDir = new File(testConnection.getPath() + "/transactions"); // Expecting that no databases/multichunks have been committed, 1 multichunk is temporary, 1 action and transaction are pending assertEquals("One pending action should exist in repo", 1, repoActionsDir.listFiles().length); assertEquals("No database should be committed in repo", 0, repoDatabasesDir.listFiles().length); assertEquals("No multichunk should be committed in repo", 0, repoMultichunksDir.listFiles().length); assertEquals("One multichunk should exist in repo as temporary", 1, repoTemporaryDir.listFiles().length); assertEquals("One pending transaction should exist in repo", 1, repoTransactionsDir.listFiles().length); // 4. Resume operation clientA.up(); // 5. Final state should be as if no failure occurred; three database versions, three complete files assertEquals("Three databases should be committed in repo", 3, repoDatabasesDir.listFiles().length); for (int fileNumber = 1; fileNumber <= 3; fileNumber++) { DatabaseRemoteFile databaseRemoteFile = new DatabaseRemoteFile("A", fileNumber); File databaseFile = new File(testConnection.getPath() + "/databases/" + databaseRemoteFile.getName()); assertTrue("Database file should exist: " + databaseFile, databaseFile.exists()); } assertEquals("Four multichunks should be committed in repo", 4, repoMultichunksDir.listFiles().length); // Tear down clientA.deleteTestData(); } }