From e1842e11c5680605391eb92538122abc5490c905 Mon Sep 17 00:00:00 2001
From: akwizgran <michael@briarproject.org>
Date: Fri, 3 May 2013 12:18:06 +0100
Subject: [PATCH] Unit tests for batched SQL UPDATE and DELETE operations.

---
 briar-tests/src/net/sf/briar/TestUtils.java   |   6 +-
 .../src/net/sf/briar/db/BasicH2Test.java      | 226 ++++++++++++++----
 2 files changed, 176 insertions(+), 56 deletions(-)

diff --git a/briar-tests/src/net/sf/briar/TestUtils.java b/briar-tests/src/net/sf/briar/TestUtils.java
index 16e05461b1..358bc0f19e 100644
--- a/briar-tests/src/net/sf/briar/TestUtils.java
+++ b/briar-tests/src/net/sf/briar/TestUtils.java
@@ -76,10 +76,8 @@ public class TestUtils {
 
 	public static String createRandomString(int length) throws Exception {
 		StringBuilder s = new StringBuilder(length);
-		for(int i = 0; i < length; i++) {
-			int letter = (int) (Math.random() * 26);
-			s.append((char) ('a' + letter));
-		}
+		for(int i = 0; i < length; i++)
+			s.append((char) ('a' + random.nextInt(26)));
 		return s.toString();
 	}
 }
diff --git a/briar-tests/src/net/sf/briar/db/BasicH2Test.java b/briar-tests/src/net/sf/briar/db/BasicH2Test.java
index e6f398a8fc..eaf4696504 100644
--- a/briar-tests/src/net/sf/briar/db/BasicH2Test.java
+++ b/briar-tests/src/net/sf/briar/db/BasicH2Test.java
@@ -23,9 +23,8 @@ import org.junit.Test;
 public class BasicH2Test extends BriarTestCase {
 
 	private static final String CREATE_TABLE =
-			"CREATE TABLE foo"
-					+ " (uniqueId BINARY(32),"
-					+ " name VARCHAR NOT NULL)";
+			"CREATE TABLE foo (uniqueId BINARY(32), name VARCHAR NOT NULL)";
+	private static final int BATCH_SIZE = 100;
 
 	private final File testDir = TestUtils.getTestDirectory();
 	private final File db = new File(testDir, "db");
@@ -41,17 +40,65 @@ public class BasicH2Test extends BriarTestCase {
 	}
 
 	@Test
-	public void testCreateTableAddRowAndRetrieve() throws Exception {
+	public void testInsertUpdateAndDelete() throws Exception {
 		// Create the table
 		createTable(connection);
-		// Generate an ID and a name
+		// Generate an ID and two names
 		byte[] id = new byte[32];
 		new Random().nextBytes(id);
-		String name = TestUtils.createRandomString(50);
-		// Insert the ID and name into the table
-		addRow(id, name);
-		// Check that the name can be retrieved using the ID
-		assertEquals(name, getName(id));
+		String oldName = TestUtils.createRandomString(50);
+		String newName = TestUtils.createRandomString(50);
+		// Insert the ID and old name into the table
+		insertRow(id, oldName);
+		// Check that the old name can be retrieved using the ID
+		assertTrue(rowExists(id));
+		assertEquals(oldName, getName(id));
+		// Update the name
+		updateRow(id, newName);
+		// Check that the new name can be retrieved using the ID
+		assertTrue(rowExists(id));
+		assertEquals(newName, getName(id));
+		// Delete the row from the table
+		assertTrue(deleteRow(id));
+		// Check that the row no longer exists
+		assertFalse(rowExists(id));
+		// Deleting the row again should have no effect
+		assertFalse(deleteRow(id));
+	}
+
+	@Test
+	public void testBatchInsertUpdateAndDelete() throws Exception {
+		// Create the table
+		createTable(connection);
+		// Generate some IDs and two sets of names
+		byte[][] ids = new byte[BATCH_SIZE][32];
+		String[] oldNames = new String[BATCH_SIZE];
+		String[] newNames = new String[BATCH_SIZE];
+		Random random = new Random();
+		for(int i = 0; i < BATCH_SIZE; i++) {
+			random.nextBytes(ids[i]);
+			oldNames[i] = TestUtils.createRandomString(50);
+			newNames[i] = TestUtils.createRandomString(50);
+		}
+		// Insert the IDs and old names into the table as a batch
+		insertBatch(ids, oldNames);
+		// Update the names as a batch
+		updateBatch(ids, newNames);
+		// Check that the new names can be retrieved using the IDs
+		for(int i = 0; i < BATCH_SIZE; i++) {
+			assertTrue(rowExists(ids[i]));
+			assertEquals(newNames[i], getName(ids[i]));
+		}
+		// Delete the rows as a batch
+		boolean[] deleted = deleteBatch(ids);
+		// Check that the rows no longer exist
+		for(int i = 0; i < BATCH_SIZE; i++) {
+			assertTrue(deleted[i]);
+			assertFalse(rowExists(ids[i]));
+		}
+		// Deleting the rows again should have no effect
+		deleted = deleteBatch(ids);
+		for(int i = 0; i < BATCH_SIZE; i++) assertFalse(deleted[i]);
 	}
 
 	@Test
@@ -77,10 +124,10 @@ public class BasicH2Test extends BriarTestCase {
 		// Create the table
 		createTable(connection);
 		// Insert the rows
-		addRow(first, "first");
-		addRow(second, "second");
-		addRow(third, "third");
-		addRow(null, "null");
+		insertRow(first, "first");
+		insertRow(second, "second");
+		insertRow(third, "third");
+		insertRow(null, "null");
 		// Check the ordering of the < operator: the null ID is not comparable
 		assertNull(getPredecessor(first));
 		assertEquals("first", getPredecessor(second));
@@ -95,24 +142,6 @@ public class BasicH2Test extends BriarTestCase {
 		assertEquals("third", names.get(3));
 	}
 
-	@Test
-	public void testCreateTableAddBatchAndRetrieve() throws Exception {
-		// Create the table
-		createTable(connection);
-		// Generate some IDs and names
-		byte[][] ids = new byte[10][32];
-		String[] names = new String[10];
-		Random random = new Random();
-		for(int i = 0; i < 10; i++) {
-			random.nextBytes(ids[i]);
-			names[i] = TestUtils.createRandomString(50);
-		}
-		// Insert the IDs and names into the table as a batch
-		addBatch(ids, names);
-		// Check that the names can be retrieved using the IDs
-		for(int i = 0; i < 10; i++) assertEquals(names[i], getName(ids[i]));
-	}
-
 	private void createTable(Connection connection) throws SQLException {
 		try {
 			Statement s = connection.createStatement();
@@ -124,7 +153,7 @@ public class BasicH2Test extends BriarTestCase {
 		}
 	}
 
-	private void addRow(byte[] id, String name) throws SQLException {
+	private void insertRow(byte[] id, String name) throws SQLException {
 		String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
 		try {
 			PreparedStatement ps = connection.prepareStatement(sql);
@@ -140,33 +169,33 @@ public class BasicH2Test extends BriarTestCase {
 		}
 	}
 
-	private String getName(byte[] id) throws SQLException {
-		String sql = "SELECT name FROM foo WHERE uniqueID = ?";
+	private boolean rowExists(byte[] id) throws SQLException {
+		assertNotNull(id);
+		String sql = "SELECT NULL FROM foo WHERE uniqueID = ?";
 		try {
 			PreparedStatement ps = connection.prepareStatement(sql);
-			if(id != null) ps.setBytes(1, id);
+			ps.setBytes(1, id);
 			ResultSet rs = ps.executeQuery();
-			assertTrue(rs.next());
-			String name = rs.getString(1);
+			boolean found = rs.next();
 			assertFalse(rs.next());
 			rs.close();
 			ps.close();
-			return name;
+			return found;
 		} catch(SQLException e) {
 			connection.close();
 			throw e;
 		}
 	}
 
-	private String getPredecessor(byte[] id) throws SQLException {
-		String sql = "SELECT name FROM foo WHERE uniqueId < ?"
-				+ " ORDER BY uniqueId DESC LIMIT ?";
+	private String getName(byte[] id) throws SQLException {
+		assertNotNull(id);
+		String sql = "SELECT name FROM foo WHERE uniqueID = ?";
 		try {
 			PreparedStatement ps = connection.prepareStatement(sql);
 			ps.setBytes(1, id);
-			ps.setInt(2, 1);
 			ResultSet rs = ps.executeQuery();
-			String name = rs.next() ? rs.getString(1) : null;
+			assertTrue(rs.next());
+			String name = rs.getString(1);
 			assertFalse(rs.next());
 			rs.close();
 			ps.close();
@@ -177,23 +206,37 @@ public class BasicH2Test extends BriarTestCase {
 		}
 	}
 
-	private List<String> getNames() throws SQLException {
-		String sql = "SELECT name FROM foo ORDER BY uniqueId";
-		List<String> names = new ArrayList<String>();
+	private void updateRow(byte[] id, String name) throws SQLException {
+		String sql = "UPDATE foo SET name = ? WHERE uniqueId = ?";
 		try {
 			PreparedStatement ps = connection.prepareStatement(sql);
-			ResultSet rs = ps.executeQuery();
-			while(rs.next()) names.add(rs.getString(1));
-			rs.close();
+			if(id == null) ps.setNull(2, BINARY);
+			else ps.setBytes(2, id);
+			ps.setString(1, name);
+			assertEquals(1, ps.executeUpdate());
 			ps.close();
-			return names;
 		} catch(SQLException e) {
 			connection.close();
 			throw e;
 		}
 	}
 
-	private void addBatch(byte[][] ids, String[] names) throws SQLException {
+	private boolean deleteRow(byte[] id) throws SQLException {
+		String sql = "DELETE FROM foo WHERE uniqueId = ?";
+		try {
+			PreparedStatement ps = connection.prepareStatement(sql);
+			if(id == null) ps.setNull(1, BINARY);
+			else ps.setBytes(1, id);
+			int affected = ps.executeUpdate();
+			ps.close();
+			return affected == 1;
+		} catch(SQLException e) {
+			connection.close();
+			throw e;
+		}
+	}
+
+	private void insertBatch(byte[][] ids, String[] names) throws SQLException {
 		assertEquals(ids.length, names.length);
 		String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)";
 		try {
@@ -216,6 +259,85 @@ public class BasicH2Test extends BriarTestCase {
 		}
 	}
 
+	private void updateBatch(byte[][] ids, String[] names) throws SQLException {
+		assertEquals(ids.length, names.length);
+		String sql = "UPDATE foo SET name = ? WHERE uniqueId = ?";
+		try {
+			PreparedStatement ps = connection.prepareStatement(sql);
+			for(int i = 0; i < ids.length; i++) {
+				if(ids[i] == null) ps.setNull(2, BINARY);
+				else ps.setBytes(2, ids[i]);
+				ps.setString(1, names[i]);
+				ps.addBatch();
+			}
+			int[] batchAffected = ps.executeBatch();
+			assertEquals(ids.length, batchAffected.length);
+			for(int i = 0; i < batchAffected.length; i++)
+				assertEquals(1, batchAffected[i]);
+			ps.close();
+		} catch(SQLException e) {
+			connection.close();
+			throw e;
+		}
+	}
+
+	private boolean[] deleteBatch(byte[][] ids) throws SQLException {
+		String sql = "DELETE FROM foo WHERE uniqueId = ?";
+		try {
+			PreparedStatement ps = connection.prepareStatement(sql);
+			for(int i = 0; i < ids.length; i++) {
+				if(ids[i] == null) ps.setNull(1, BINARY);
+				else ps.setBytes(1, ids[i]);
+				ps.addBatch();
+			}
+			int[] batchAffected = ps.executeBatch();
+			assertEquals(ids.length, batchAffected.length);
+			boolean[] ret = new boolean[ids.length];
+			for(int i = 0; i < batchAffected.length; i++)
+				ret[i] = batchAffected[i] == 1;
+			ps.close();
+			return ret;
+		} catch(SQLException e) {
+			connection.close();
+			throw e;
+		}
+	}
+
+	private String getPredecessor(byte[] id) throws SQLException {
+		String sql = "SELECT name FROM foo WHERE uniqueId < ?"
+				+ " ORDER BY uniqueId DESC LIMIT ?";
+		try {
+			PreparedStatement ps = connection.prepareStatement(sql);
+			ps.setBytes(1, id);
+			ps.setInt(2, 1);
+			ResultSet rs = ps.executeQuery();
+			String name = rs.next() ? rs.getString(1) : null;
+			assertFalse(rs.next());
+			rs.close();
+			ps.close();
+			return name;
+		} catch(SQLException e) {
+			connection.close();
+			throw e;
+		}
+	}
+
+	private List<String> getNames() throws SQLException {
+		String sql = "SELECT name FROM foo ORDER BY uniqueId";
+		List<String> names = new ArrayList<String>();
+		try {
+			PreparedStatement ps = connection.prepareStatement(sql);
+			ResultSet rs = ps.executeQuery();
+			while(rs.next()) names.add(rs.getString(1));
+			rs.close();
+			ps.close();
+			return names;
+		} catch(SQLException e) {
+			connection.close();
+			throw e;
+		}
+	}
+
 	@After
 	public void tearDown() throws Exception {
 		if(connection != null) connection.close();
-- 
GitLab