Skip to content
Snippets Groups Projects
Commit 75d53598 authored by akwizgran's avatar akwizgran
Browse files

Merge branch 'validation-manager-tests' into 'master'

Unit tests for ValidationManagerImpl

Unit test for the validation manager. I also changed the way the validation manager loads unvalidated messages - instead of using a single DB task to load all unvalidated messages, it loads a list of message IDs and then loads each message in a separate task. This prevents the DatabaseExecutor from being blocked by a long-running task if there are lots of messages to validate.

See merge request !113
parents 9dec498e 476b1edb
No related branches found
No related tags found
No related merge requests found
......@@ -9,6 +9,7 @@ import org.briarproject.api.db.DatabaseExecutor;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Metadata;
import org.briarproject.api.db.NoSuchGroupException;
import org.briarproject.api.db.NoSuchMessageException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener;
......@@ -23,8 +24,10 @@ import org.briarproject.api.sync.MessageValidator;
import org.briarproject.api.sync.ValidationManager;
import org.briarproject.util.ByteUtils;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.Executor;
......@@ -81,19 +84,48 @@ class ValidationManagerImpl implements ValidationManager, Service,
dbExecutor.execute(new Runnable() {
public void run() {
try {
// TODO: Don't do all of this in a single DB task
Queue<MessageId> unvalidated = new LinkedList<MessageId>();
Transaction txn = db.startTransaction();
try {
for (MessageId id : db.getMessagesToValidate(txn, c)) {
byte[] raw = db.getRawMessage(txn, id);
Message m = parseMessage(id, raw);
Group g = db.getGroup(txn, m.getGroupId());
validateMessage(m, g);
}
unvalidated.addAll(db.getMessagesToValidate(txn, c));
txn.setComplete();
} finally {
db.endTransaction(txn);
}
validateNextMessage(unvalidated);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
private void validateNextMessage(final Queue<MessageId> unvalidated) {
if (unvalidated.isEmpty()) return;
dbExecutor.execute(new Runnable() {
public void run() {
try {
Message m = null;
Group g = null;
Transaction txn = db.startTransaction();
try {
MessageId id = unvalidated.poll();
byte[] raw = db.getRawMessage(txn, id);
m = parseMessage(id, raw);
g = db.getGroup(txn, m.getGroupId());
txn.setComplete();
} catch (NoSuchMessageException e) {
LOG.info("Message removed before validation");
// Continue to next message
} catch (NoSuchGroupException e) {
LOG.info("Group removed before validation");
// Continue to next message
} finally {
db.endTransaction(txn);
}
if (m != null && g != null) validateMessage(m, g);
validateNextMessage(unvalidated);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
......@@ -158,21 +190,23 @@ class ValidationManagerImpl implements ValidationManager, Service,
if (e instanceof MessageAddedEvent) {
// Validate the message if it wasn't created locally
MessageAddedEvent m = (MessageAddedEvent) e;
if (m.getContactId() != null) loadGroup(m.getMessage());
if (m.getContactId() != null) loadGroupAndValidate(m.getMessage());
}
}
private void loadGroup(final Message m) {
private void loadGroupAndValidate(final Message m) {
dbExecutor.execute(new Runnable() {
public void run() {
try {
Group g;
Transaction txn = db.startTransaction();
try {
validateMessage(m, db.getGroup(txn, m.getGroupId()));
g = db.getGroup(txn, m.getGroupId());
txn.setComplete();
} finally {
db.endTransaction(txn);
}
validateMessage(m, g);
} catch (NoSuchGroupException e) {
LOG.info("Group removed before validation");
} catch (DbException e) {
......
package org.briarproject.plugins;
package org.briarproject;
import java.util.concurrent.Executor;
......
package org.briarproject.plugins.file;
import org.briarproject.BriarTestCase;
import org.briarproject.ImmediateExecutor;
import org.briarproject.TestUtils;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.plugins.TransportConnectionWriter;
import org.briarproject.api.plugins.simplex.SimplexPluginCallback;
import org.briarproject.plugins.ImmediateExecutor;
import org.briarproject.plugins.file.RemovableDriveMonitor.Callback;
import org.jmock.Expectations;
import org.jmock.Mockery;
......
......@@ -4,6 +4,7 @@ import com.google.inject.Guice;
import com.google.inject.Injector;
import org.briarproject.BriarTestCase;
import org.briarproject.ImmediateExecutor;
import org.briarproject.TestDatabaseModule;
import org.briarproject.TestSystemModule;
import org.briarproject.TestUtils;
......@@ -43,7 +44,6 @@ import org.briarproject.event.EventModule;
import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule;
import org.briarproject.plugins.ImmediateExecutor;
import org.briarproject.transport.TransportModule;
import org.junit.After;
import org.junit.Before;
......
package org.briarproject.sync;
import org.briarproject.BriarTestCase;
import org.briarproject.ImmediateExecutor;
import org.briarproject.TestUtils;
import org.briarproject.api.TransportId;
import org.briarproject.api.contact.ContactId;
......@@ -10,7 +11,6 @@ import org.briarproject.api.event.EventBus;
import org.briarproject.api.sync.Ack;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.PacketWriter;
import org.briarproject.plugins.ImmediateExecutor;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
......
package org.briarproject.sync;
import org.briarproject.BriarTestCase;
import org.briarproject.ImmediateExecutor;
import org.briarproject.TestUtils;
import org.briarproject.api.UniqueId;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.Metadata;
import org.briarproject.api.db.NoSuchGroupException;
import org.briarproject.api.db.NoSuchMessageException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.event.MessageAddedEvent;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.Message;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.MessageValidator;
import org.briarproject.api.sync.ValidationManager.ValidationHook;
import org.briarproject.util.ByteUtils;
import org.jmock.Expectations;
import org.jmock.Mockery;
import org.junit.Test;
import static org.junit.Assert.fail;
import java.util.Arrays;
import java.util.concurrent.Executor;
public class ValidationManagerImplTest extends BriarTestCase {
private final ClientId clientId = new ClientId(TestUtils.getRandomId());
private final MessageId messageId = new MessageId(TestUtils.getRandomId());
private final MessageId messageId1 = new MessageId(TestUtils.getRandomId());
private final GroupId groupId = new GroupId(TestUtils.getRandomId());
private final byte[] descriptor = new byte[32];
private final Group group = new Group(groupId, clientId, descriptor);
private final long timestamp = System.currentTimeMillis();
private final byte[] raw = new byte[123];
private final Message message = new Message(messageId, groupId, timestamp,
raw);
private final Message message1 = new Message(messageId1, groupId, timestamp,
raw);
private final Metadata metadata = new Metadata();
private final ContactId contactId = new ContactId(234);
public ValidationManagerImplTest() {
// Encode the messages
System.arraycopy(groupId.getBytes(), 0, raw, 0, UniqueId.LENGTH);
ByteUtils.writeUint64(timestamp, raw, UniqueId.LENGTH);
}
@Test
public void testMessagesAreValidatedAtStartup() throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageValidator validator = context.mock(MessageValidator.class);
final ValidationHook hook = context.mock(ValidationHook.class);
final Transaction txn = new Transaction(null);
final Transaction txn1 = new Transaction(null);
final Transaction txn2 = new Transaction(null);
final Transaction txn3 = new Transaction(null);
final Transaction txn4 = new Transaction(null);
context.checking(new Expectations() {{
// Get messages to validate
oneOf(db).startTransaction();
will(returnValue(txn));
oneOf(db).getMessagesToValidate(txn, clientId);
will(returnValue(Arrays.asList(messageId, messageId1)));
oneOf(db).endTransaction(txn);
// Load the first raw message and group
oneOf(db).startTransaction();
will(returnValue(txn1));
oneOf(db).getRawMessage(txn1, messageId);
will(returnValue(raw));
oneOf(db).getGroup(txn1, groupId);
will(returnValue(group));
oneOf(db).endTransaction(txn1);
// Validate the first message: valid
oneOf(validator).validateMessage(message, group);
will(returnValue(metadata));
// Store the validation result for the first message
oneOf(db).startTransaction();
will(returnValue(txn2));
oneOf(db).mergeMessageMetadata(txn2, messageId, metadata);
oneOf(db).setMessageValid(txn2, message, clientId, true);
oneOf(db).setMessageShared(txn2, message, true);
// Call the hook for the first message
oneOf(hook).validatingMessage(txn2, message, clientId, metadata);
oneOf(db).endTransaction(txn2);
// Load the second raw message and group
oneOf(db).startTransaction();
will(returnValue(txn3));
oneOf(db).getRawMessage(txn3, messageId1);
will(returnValue(raw));
oneOf(db).getGroup(txn3, groupId);
will(returnValue(group));
oneOf(db).endTransaction(txn3);
// Validate the second message: invalid
oneOf(validator).validateMessage(message1, group);
will(returnValue(null));
// Store the validation result for the second message
oneOf(db).startTransaction();
will(returnValue(txn4));
oneOf(db).setMessageValid(txn4, message1, clientId, false);
oneOf(db).endTransaction(txn4);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor);
vm.registerMessageValidator(clientId, validator);
vm.registerValidationHook(hook);
vm.start();
context.assertIsSatisfied();
}
@Test
public void testValidationContinuesAfterNoSuchMessageException()
throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageValidator validator = context.mock(MessageValidator.class);
final ValidationHook hook = context.mock(ValidationHook.class);
final Transaction txn = new Transaction(null);
final Transaction txn1 = new Transaction(null);
final Transaction txn2 = new Transaction(null);
final Transaction txn3 = new Transaction(null);
context.checking(new Expectations() {{
// Get messages to validate
oneOf(db).startTransaction();
will(returnValue(txn));
oneOf(db).getMessagesToValidate(txn, clientId);
will(returnValue(Arrays.asList(messageId, messageId1)));
oneOf(db).endTransaction(txn);
// Load the first raw message - *gasp* it's gone!
oneOf(db).startTransaction();
will(returnValue(txn1));
oneOf(db).getRawMessage(txn1, messageId);
will(throwException(new NoSuchMessageException()));
oneOf(db).endTransaction(txn1);
// Load the second raw message and group
oneOf(db).startTransaction();
will(returnValue(txn2));
oneOf(db).getRawMessage(txn2, messageId1);
will(returnValue(raw));
oneOf(db).getGroup(txn2, groupId);
will(returnValue(group));
oneOf(db).endTransaction(txn2);
// Validate the second message: invalid
oneOf(validator).validateMessage(message1, group);
will(returnValue(null));
// Store the validation result for the second message
oneOf(db).startTransaction();
will(returnValue(txn3));
oneOf(db).setMessageValid(txn3, message1, clientId, false);
oneOf(db).endTransaction(txn3);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor);
vm.registerMessageValidator(clientId, validator);
vm.registerValidationHook(hook);
vm.start();
context.assertIsSatisfied();
}
@Test
public void testUnitTestsExist() {
fail(); // FIXME: Write tests
public void testValidationContinuesAfterNoSuchGroupException()
throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageValidator validator = context.mock(MessageValidator.class);
final ValidationHook hook = context.mock(ValidationHook.class);
final Transaction txn = new Transaction(null);
final Transaction txn1 = new Transaction(null);
final Transaction txn2 = new Transaction(null);
final Transaction txn3 = new Transaction(null);
context.checking(new Expectations() {{
// Get messages to validate
oneOf(db).startTransaction();
will(returnValue(txn));
oneOf(db).getMessagesToValidate(txn, clientId);
will(returnValue(Arrays.asList(messageId, messageId1)));
oneOf(db).endTransaction(txn);
// Load the first raw message
oneOf(db).startTransaction();
will(returnValue(txn1));
oneOf(db).getRawMessage(txn1, messageId);
will(returnValue(raw));
// Load the group - *gasp* it's gone!
oneOf(db).getGroup(txn1, groupId);
will(throwException(new NoSuchGroupException()));
oneOf(db).endTransaction(txn1);
// Load the second raw message and group
oneOf(db).startTransaction();
will(returnValue(txn2));
oneOf(db).getRawMessage(txn2, messageId1);
will(returnValue(raw));
oneOf(db).getGroup(txn2, groupId);
will(returnValue(group));
oneOf(db).endTransaction(txn2);
// Validate the second message: invalid
oneOf(validator).validateMessage(message1, group);
will(returnValue(null));
// Store the validation result for the second message
oneOf(db).startTransaction();
will(returnValue(txn3));
oneOf(db).setMessageValid(txn3, message1, clientId, false);
oneOf(db).endTransaction(txn3);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor);
vm.registerMessageValidator(clientId, validator);
vm.registerValidationHook(hook);
vm.start();
context.assertIsSatisfied();
}
@Test
public void testNonLocalMessagesAreValidatedWhenAdded() throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageValidator validator = context.mock(MessageValidator.class);
final ValidationHook hook = context.mock(ValidationHook.class);
final Transaction txn = new Transaction(null);
final Transaction txn1 = new Transaction(null);
context.checking(new Expectations() {{
// Load the group
oneOf(db).startTransaction();
will(returnValue(txn));
oneOf(db).getGroup(txn, groupId);
will(returnValue(group));
oneOf(db).endTransaction(txn);
// Validate the message: valid
oneOf(validator).validateMessage(message, group);
will(returnValue(metadata));
// Store the validation result
oneOf(db).startTransaction();
will(returnValue(txn1));
oneOf(db).mergeMessageMetadata(txn1, messageId, metadata);
oneOf(db).setMessageValid(txn1, message, clientId, true);
oneOf(db).setMessageShared(txn1, message, true);
// Call the hook
oneOf(hook).validatingMessage(txn1, message, clientId, metadata);
oneOf(db).endTransaction(txn1);
}});
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor);
vm.registerMessageValidator(clientId, validator);
vm.registerValidationHook(hook);
vm.eventOccurred(new MessageAddedEvent(message, contactId));
context.assertIsSatisfied();
}
@Test
public void testLocalMessagesAreNotValidatedWhenAdded() throws Exception {
Mockery context = new Mockery();
final DatabaseComponent db = context.mock(DatabaseComponent.class);
final Executor dbExecutor = new ImmediateExecutor();
final Executor cryptoExecutor = new ImmediateExecutor();
final MessageValidator validator = context.mock(MessageValidator.class);
final ValidationHook hook = context.mock(ValidationHook.class);
ValidationManagerImpl vm = new ValidationManagerImpl(db, dbExecutor,
cryptoExecutor);
vm.registerMessageValidator(clientId, validator);
vm.registerValidationHook(hook);
vm.eventOccurred(new MessageAddedEvent(message, null));
context.assertIsSatisfied();
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment