gt: 		for (int i = 1; i <= 21; i++) {
context: /*
   * 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.assertNotNull;
  import static org.syncany.tests.util.TestAssertUtil.assertConflictingFileExists;
  import static org.syncany.tests.util.TestAssertUtil.assertFileEquals;
  import static org.syncany.tests.util.TestAssertUtil.assertFileListEquals;
  import static org.syncany.tests.util.TestAssertUtil.assertSqlDatabaseEquals;
  
  import java.util.Iterator;
  import java.util.List;
  
  import org.junit.Test;
  import org.syncany.config.Config;
  import org.syncany.database.DatabaseConnectionFactory;
  import org.syncany.database.DatabaseVersion;
  import org.syncany.database.PartialFileHistory;
  import org.syncany.database.dao.ChunkSqlDao;
  import org.syncany.database.dao.DatabaseVersionSqlDao;
  import org.syncany.database.dao.FileContentSqlDao;
  import org.syncany.database.dao.FileHistorySqlDao;
  import org.syncany.database.dao.FileVersionSqlDao;
  import org.syncany.database.dao.MultiChunkSqlDao;
  import org.syncany.operations.ChangeSet;
  import org.syncany.operations.cleanup.CleanupOperationOptions;
  import org.syncany.operations.status.StatusOperationOptions;
  import org.syncany.operations.status.StatusOperationResult;
  import org.syncany.operations.up.UpOperationOptions;
  import org.syncany.plugins.transfer.TransferSettings;
  import org.syncany.tests.util.TestAssertUtil;
  import org.syncany.tests.util.TestClient;
  import org.syncany.tests.util.TestCollectionUtil;
  import org.syncany.tests.util.TestConfigUtil;
  import org.syncany.tests.util.TestSqlUtil;
  
  public class DirtyDatabaseScenarioTest {
  	@Test
  	public void testDirtyDatabase() throws Exception {
  		// Setup
  		TransferSettings testConnection = TestConfigUtil.createTestLocalConnection();
  
  		TestClient clientA = new TestClient("A", testConnection);
  		TestClient clientB = new TestClient("B", testConnection);
  
  		// Run
  		UpOperationOptions upOptionsForceEnabled = new UpOperationOptions();
  		upOptionsForceEnabled.setForceUploadEnabled(true);
  
  		clientA.createNewFile("A-file1.jpg", 50 * 1024);
  		clientA.up(upOptionsForceEnabled);
  
  		clientB.createNewFile("A-file1.jpg", 51 * 1024);
  		clientB.up(upOptionsForceEnabled);
  
  		clientB.down(); // This creates a dirty database
  
  		// Test (for dirty database existence)
  		Config configB = clientB.getConfig();
  		java.sql.Connection databaseConnectionB = configB.createDatabaseConnection();
  
  		ChunkSqlDao chunkDao = new ChunkSqlDao(databaseConnectionB);
  		MultiChunkSqlDao multiChunkDao = new MultiChunkSqlDao(databaseConnectionB);
  		FileVersionSqlDao fileVersionDao = new FileVersionSqlDao(databaseConnectionB);
  		FileHistorySqlDao fileHistoryDao = new FileHistorySqlDao(databaseConnectionB, fileVersionDao);
  		FileContentSqlDao fileContentDao = new FileContentSqlDao(databaseConnectionB);
  		DatabaseVersionSqlDao databaseVersionDao = new DatabaseVersionSqlDao(databaseConnectionB, chunkDao, fileContentDao, fileVersionDao,
  				fileHistoryDao, multiChunkDao);
  
  		Iterator<DatabaseVersion> databaseVersionsDirtyB = databaseVersionDao.getDirtyDatabaseVersions();
  		List<DatabaseVersion> databaseVersionsDirtyListB = TestCollectionUtil.toList(databaseVersionsDirtyB);
  
  		assertEquals(1, databaseVersionsDirtyListB.size());
  
  		DatabaseVersion dirtyDatabaseVersionB = databaseVersionsDirtyListB.get(0);
  		assertNotNull(dirtyDatabaseVersionB);
  		assertEquals(1, dirtyDatabaseVersionB.getFileHistories().size());
  
  		PartialFileHistory fileHistoryFile1B = dirtyDatabaseVersionB.getFileHistories().iterator().next();
  		assertNotNull(fileHistoryFile1B);
  		assertEquals(1, fileHistoryFile1B.getFileVersions().size());
  		assertEquals("A-file1.jpg", fileHistoryFile1B.getLastVersion().getPath());
  
  		assertFileEquals(clientA.getLocalFile("A-file1.jpg"), clientB.getLocalFile("A-file1.jpg"));
  		assertConflictingFileExists("A-file1.jpg", clientB.getLocalFilesExcludeLockedAndNoRead());
  
  		// Run (part 2)
  		clientB.up(); // This deletes the dirty database file
  
  		Iterator<DatabaseVersion> databaseVersionsDirtyB2 = databaseVersionDao.getDirtyDatabaseVersions();
  		List<DatabaseVersion> databaseVersionsDirtyListB2 = TestCollectionUtil.toList(databaseVersionsDirtyB2);
  
  		assertEquals(0, databaseVersionsDirtyListB2.size());
  
  		// Run (part 3)
  		clientA.down(); // This pulls down the conflicting file
  		assertFileListEquals(clientA.getLocalFilesExcludeLockedAndNoRead(), clientB.getLocalFilesExcludeLockedAndNoRead());
  		assertSqlDatabaseEquals(clientA.getDatabaseFile(), clientB.getDatabaseFile());
  		assertConflictingFileExists("A-file1.jpg", clientA.getLocalFilesExcludeLockedAndNoRead());
  
  		// Tear down
  		clientA.deleteTestData();
  		clientB.deleteTestData();
  	}
  
  	@Test
  	public void testDirtyCleanupDirty() 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);
  		TestClient clientD = new TestClient("D", testConnection);
  
  		StatusOperationOptions statusOptions = new StatusOperationOptions();
  		statusOptions.setForceChecksum(true);
  
  		UpOperationOptions upOptionsForceEnabled = new UpOperationOptions();
  		upOptionsForceEnabled.setStatusOptions(statusOptions);
  		upOptionsForceEnabled.setForceUploadEnabled(true);
  
  		CleanupOperationOptions cleanupOptions = new CleanupOperationOptions();
  		cleanupOptions.setMinSecondsBetweenCleanups(0);
  		cleanupOptions.setForce(true);
  
  		// Run
  
  		//// 1. CREATE FIRST DIRTY VERSION
  
  		clientA.createNewFile("A-file1.jpg", 50 * 1024);
  		clientA.up(upOptionsForceEnabled); // (A1)
  
  		clientB.down();
  		clientB.changeFile("A-file1.jpg");
  
  		clientB.up(upOptionsForceEnabled); // (A1,B1)
  
  		clientA.down();
  
  		clientA.changeFile("A-file1.jpg"); // conflict (winner)
  		clientA.up(upOptionsForceEnabled); // (A2,B1)
  
  		clientB.changeFile("A-file1.jpg"); // conflict (loser)
  		clientB.up(upOptionsForceEnabled);
  
  		clientA.createNewFolder("new folder at A"); // don't care about the conflict, just continue
  		clientA.up(upOptionsForceEnabled); // (A3,B1)
  
  		clientB.createNewFolder("new folder at B"); // don't care about the conflict, just continue
  		clientB.up(upOptionsForceEnabled);
  
  		clientA.down(); // resolve conflict (wins, no DIRTY)
  
  		java.sql.Connection databaseConnectionA = DatabaseConnectionFactory.createConnection(clientA.getDatabaseFile());
  		assertEquals("0", TestSqlUtil.runSqlSelect("select count(*) from databaseversion where status='DIRTY'", databaseConnectionA));
  
  		clientB.down(); // resolve conflict (loses, creates DIRTY version)
  
  		java.sql.Connection databaseConnectionB = DatabaseConnectionFactory.createConnection(clientB.getDatabaseFile());
  		assertEquals("2", TestSqlUtil.runSqlSelect("select count(*) from databaseversion where status='DIRTY'", databaseConnectionB));
  		assertEquals("(A1,B2)\n(A1,B3)",
  				TestSqlUtil.runSqlSelect("select vectorclock_serialized from databaseversion where status='DIRTY' order by id", databaseConnectionB));
  		assertEquals("(A1)\n(A1,B1)\n(A2,B1)\n(A3,B1)",
  				TestSqlUtil.runSqlSelect("select vectorclock_serialized from databaseversion where status<>'DIRTY' order by id", databaseConnectionB));
  
  		StatusOperationResult statusResultBAfterDirty = clientB.status();
  		assertNotNull(statusResultBAfterDirty);
  
  		ChangeSet changeSetBAfterDirty = statusResultBAfterDirty.getChangeSet();
  		assertEquals(2, changeSetBAfterDirty.getNewFiles().size());
  		TestAssertUtil.assertConflictingFileExists("A-file1.jpg", clientB.getLocalFiles());
  
  		clientB.up(upOptionsForceEnabled); // (A3,B2)
  		assertEquals("0", TestSqlUtil.runSqlSelect("select count(*) from databaseversion where status='DIRTY'", databaseConnectionB));
  
  		assertEquals("4", TestSqlUtil.runSqlSelect("select count(*) from databaseversion", databaseConnectionA)); // (A1), (A1,B1), (A2,B1), (A3,B1)
  		assertEquals("(A1)\n(A1,B1)\n(A2,B1)\n(A3,B1)",
  				TestSqlUtil.runSqlSelect("select vectorclock_serialized from databaseversion order by id", databaseConnectionA));
  
  		assertEquals("5", TestSqlUtil.runSqlSelect("select count(*) from databaseversion", databaseConnectionB));
  		assertEquals("(A1)\n(A1,B1)\n(A2,B1)\n(A3,B1)\n(A3,B4)",
  				TestSqlUtil.runSqlSelect("select vectorclock_serialized from databaseversion order by id", databaseConnectionB));
  
  		//// 2. NOW THAT CLIENT B RESOLVED IT, A GETS DIRTY
  
  		clientA.changeFile("A-file1.jpg"); // No 'down'! This version will become DIRTY
  		clientA.createNewFile("dirty1");
  		clientA.up(upOptionsForceEnabled);
  
  		clientA.changeFile("A-file1.jpg"); // No 'down'! This version will become DIRTY
  		clientA.createNewFile("dirty2");
  		clientA.up(upOptionsForceEnabled);
  
  		clientA.changeFile("A-file1.jpg"); // No 'down'! This version will become DIRTY
  		clientA.createNewFile("dirty3");
  		clientA.up(upOptionsForceEnabled);
  
  		clientA.changeFile("A-file1.jpg"); // No 'down'! This version will become DIRTY
  		clientA.createNewFile("dirty4");
  		clientA.up(upOptionsForceEnabled);
  
  		clientA.changeFile("A-file1.jpg"); // No 'down'! This version will become DIRTY
  		clientA.createNewFile("dirty5");
  		clientA.up(upOptionsForceEnabled);
  
  		clientA.changeFile("A-file1.jpg"); // No 'down'! This version will become DIRTY
  		clientA.createNewFile("dirty6");
  		clientA.up(upOptionsForceEnabled);
  
  		clientA.changeFile("A-file1.jpg"); // No 'down'! This version will become DIRTY
  		clientA.createNewFile("dirty7");
  		clientA.up(upOptionsForceEnabled);
  
  		assertEquals("11", TestSqlUtil.runSqlSelect("select count(*) from databaseversion", databaseConnectionA));
  
  		clientA.down();
  		assertEquals("12", TestSqlUtil.runSqlSelect("select count(*) from databaseversion", databaseConnectionA));
  		assertEquals("7", TestSqlUtil.runSqlSelect("select count(*) from databaseversion where status='DIRTY'", databaseConnectionA));
  		assertEquals("5", TestSqlUtil.runSqlSelect("select count(*) from databaseversion where status<>'DIRTY'", databaseConnectionA));
  		assertEquals("(A1)\n(A1,B1)\n(A2,B1)\n(A3,B1)\n(A3,B4)",
  				TestSqlUtil.runSqlSelect("select vectorclock_serialized from databaseversion where status<>'DIRTY' order by id", databaseConnectionA));
  
  		clientB.down(); // Does nothing; A versions lose against (A3,B2) // same as above!
  		assertEquals("5", TestSqlUtil.runSqlSelect("select count(*) from databaseversion", databaseConnectionB));
  		assertEquals("(A1)\n(A1,B1)\n(A2,B1)\n(A3,B1)\n(A3,B4)",
  				TestSqlUtil.runSqlSelect("select vectorclock_serialized from databaseversion order by id", databaseConnectionB));
  
  		//// 3. NEW CLIENT JOINS
  
  		clientC.down();
  		TestAssertUtil.assertSqlDatabaseEquals(clientB.getDatabaseFile(), clientC.getDatabaseFile());
  
  		//// 4. FORCE MERGE DATABASES ON CLIENT A
  
  		clientA.deleteFile("dirty1");
  		clientA.deleteFile("dirty2");
  
  		clientA.up(upOptionsForceEnabled); // upload DIRTY version
  		assertEquals("6", TestSqlUtil.runSqlSelect("select count(*) from databaseversion", databaseConnectionA));
  		assertEquals("0", TestSqlUtil.runSqlSelect("select count(*) from databaseversion where status='DIRTY'", databaseConnectionA));
  		assertEquals("6", TestSqlUtil.runSqlSelect("select count(*) from databaseversion where status<>'DIRTY'", databaseConnectionA));
  
  		clientA.createNewFile("A-file2.jpg");
  
  		int cleanupEveryXUps = 7; // For every X up's call 'cleanup' ("X" is larger than the max. length of file versions in a history)
  
  