mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-15 20:29:52 +01:00
Separate the sync layer from its clients. #112
This commit is contained in:
@@ -6,18 +6,14 @@ import com.google.inject.Injector;
|
||||
import org.briarproject.api.TransportId;
|
||||
import org.briarproject.api.TransportProperties;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.crypto.KeyPair;
|
||||
import org.briarproject.api.crypto.SecretKey;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.sync.Ack;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.GroupFactory;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.sync.MessageFactory;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.api.sync.MessageVerifier;
|
||||
import org.briarproject.api.sync.Offer;
|
||||
import org.briarproject.api.sync.PacketReader;
|
||||
import org.briarproject.api.sync.PacketReaderFactory;
|
||||
@@ -26,7 +22,6 @@ import org.briarproject.api.sync.PacketWriterFactory;
|
||||
import org.briarproject.api.sync.Request;
|
||||
import org.briarproject.api.sync.SubscriptionUpdate;
|
||||
import org.briarproject.api.sync.TransportUpdate;
|
||||
import org.briarproject.api.sync.UnverifiedMessage;
|
||||
import org.briarproject.api.transport.StreamContext;
|
||||
import org.briarproject.api.transport.StreamReaderFactory;
|
||||
import org.briarproject.api.transport.StreamWriterFactory;
|
||||
@@ -46,6 +41,7 @@ import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.briarproject.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -58,7 +54,6 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
private final StreamWriterFactory streamWriterFactory;
|
||||
private final PacketReaderFactory packetReaderFactory;
|
||||
private final PacketWriterFactory packetWriterFactory;
|
||||
private final MessageVerifier messageVerifier;
|
||||
|
||||
private final ContactId contactId;
|
||||
private final SecretKey tagKey, headerKey;
|
||||
@@ -78,29 +73,22 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
streamWriterFactory = i.getInstance(StreamWriterFactory.class);
|
||||
packetReaderFactory = i.getInstance(PacketReaderFactory.class);
|
||||
packetWriterFactory = i.getInstance(PacketWriterFactory.class);
|
||||
messageVerifier = i.getInstance(MessageVerifier.class);
|
||||
contactId = new ContactId(234);
|
||||
// Create the transport keys
|
||||
tagKey = TestUtils.createSecretKey();
|
||||
headerKey = TestUtils.createSecretKey();
|
||||
// Create a group
|
||||
GroupFactory groupFactory = i.getInstance(GroupFactory.class);
|
||||
group = groupFactory.createGroup("Group");
|
||||
// Create an author
|
||||
AuthorFactory authorFactory = i.getInstance(AuthorFactory.class);
|
||||
CryptoComponent crypto = i.getInstance(CryptoComponent.class);
|
||||
KeyPair authorKeyPair = crypto.generateSignatureKeyPair();
|
||||
Author author = authorFactory.createAuthor("Alice",
|
||||
authorKeyPair.getPublic().getEncoded());
|
||||
// Create two messages to the group: one anonymous, one pseudonymous
|
||||
ClientId clientId = new ClientId(TestUtils.getRandomId());
|
||||
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
|
||||
group = groupFactory.createGroup(clientId, descriptor);
|
||||
// Add two messages to the group
|
||||
MessageFactory messageFactory = i.getInstance(MessageFactory.class);
|
||||
String contentType = "text/plain";
|
||||
long timestamp = System.currentTimeMillis();
|
||||
String messageBody = "Hello world";
|
||||
message = messageFactory.createAnonymousMessage(null, group,
|
||||
"text/plain", timestamp, messageBody.getBytes("UTF-8"));
|
||||
message1 = messageFactory.createPseudonymousMessage(null, group,
|
||||
author, authorKeyPair.getPrivate(), contentType, timestamp,
|
||||
message = messageFactory.createMessage(group.getId(), timestamp,
|
||||
messageBody.getBytes("UTF-8"));
|
||||
message1 = messageFactory.createMessage(group.getId(), timestamp,
|
||||
messageBody.getBytes("UTF-8"));
|
||||
messageIds = Arrays.asList(message.getId(), message1.getId());
|
||||
// Create some transport properties
|
||||
@@ -125,8 +113,8 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
|
||||
packetWriter.writeAck(new Ack(messageIds));
|
||||
|
||||
packetWriter.writeMessage(message.getSerialised());
|
||||
packetWriter.writeMessage(message1.getSerialised());
|
||||
packetWriter.writeMessage(message.getRaw());
|
||||
packetWriter.writeMessage(message1.getRaw());
|
||||
|
||||
packetWriter.writeOffer(new Offer(messageIds));
|
||||
|
||||
@@ -163,11 +151,11 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
|
||||
// Read and verify the messages
|
||||
assertTrue(packetReader.hasMessage());
|
||||
UnverifiedMessage m = packetReader.readMessage();
|
||||
checkMessageEquality(message, messageVerifier.verifyMessage(m));
|
||||
Message m = packetReader.readMessage();
|
||||
checkMessageEquality(message, m);
|
||||
assertTrue(packetReader.hasMessage());
|
||||
m = packetReader.readMessage();
|
||||
checkMessageEquality(message1, messageVerifier.verifyMessage(m));
|
||||
checkMessageEquality(message1, m);
|
||||
assertFalse(packetReader.hasMessage());
|
||||
|
||||
// Read the offer
|
||||
@@ -198,10 +186,7 @@ public class ProtocolIntegrationTest extends BriarTestCase {
|
||||
|
||||
private void checkMessageEquality(Message m1, Message m2) {
|
||||
assertEquals(m1.getId(), m2.getId());
|
||||
assertEquals(m1.getParent(), m2.getParent());
|
||||
assertEquals(m1.getGroup(), m2.getGroup());
|
||||
assertEquals(m1.getAuthor(), m2.getAuthor());
|
||||
assertEquals(m1.getTimestamp(), m2.getTimestamp());
|
||||
assertArrayEquals(m1.getSerialised(), m2.getSerialised());
|
||||
assertArrayEquals(m1.getRaw(), m2.getRaw());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
package org.briarproject;
|
||||
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
|
||||
public class TestMessage implements Message {
|
||||
|
||||
private final MessageId id, parent;
|
||||
private final Group group;
|
||||
private final Author author;
|
||||
private final String contentType;
|
||||
private final long timestamp;
|
||||
private final byte[] raw;
|
||||
private final int bodyStart, bodyLength;
|
||||
|
||||
public TestMessage(MessageId id, MessageId parent, Group group,
|
||||
Author author, String contentType, long timestamp, byte[] raw) {
|
||||
this(id, parent, group, author, contentType, timestamp, raw, 0,
|
||||
raw.length);
|
||||
}
|
||||
|
||||
public TestMessage(MessageId id, MessageId parent, Group group,
|
||||
Author author, String contentType, long timestamp, byte[] raw,
|
||||
int bodyStart, int bodyLength) {
|
||||
this.id = id;
|
||||
this.parent = parent;
|
||||
this.group = group;
|
||||
this.author = author;
|
||||
this.contentType = contentType;
|
||||
this.timestamp = timestamp;
|
||||
this.raw = raw;
|
||||
this.bodyStart = bodyStart;
|
||||
this.bodyLength = bodyLength;
|
||||
}
|
||||
|
||||
public MessageId getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public MessageId getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public Group getGroup() {
|
||||
return group;
|
||||
}
|
||||
|
||||
public Author getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public String getContentType() {
|
||||
return contentType;
|
||||
}
|
||||
|
||||
public long getTimestamp() {
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
public byte[] getSerialised() {
|
||||
return raw;
|
||||
}
|
||||
|
||||
public int getBodyStart() {
|
||||
return bodyStart;
|
||||
}
|
||||
|
||||
public int getBodyLength() {
|
||||
return bodyLength;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return id.hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof Message && id.equals(((Message)o).getId());
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.db;
|
||||
|
||||
import org.briarproject.BriarTestCase;
|
||||
import org.briarproject.TestMessage;
|
||||
import org.briarproject.TestUtils;
|
||||
import org.briarproject.api.Settings;
|
||||
import org.briarproject.api.TransportId;
|
||||
@@ -10,8 +9,10 @@ import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.crypto.SecretKey;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.Metadata;
|
||||
import org.briarproject.api.db.NoSuchContactException;
|
||||
import org.briarproject.api.db.NoSuchLocalAuthorException;
|
||||
import org.briarproject.api.db.NoSuchMessageException;
|
||||
import org.briarproject.api.db.NoSuchSubscriptionException;
|
||||
import org.briarproject.api.db.NoSuchTransportException;
|
||||
import org.briarproject.api.event.ContactAddedEvent;
|
||||
@@ -25,6 +26,7 @@ import org.briarproject.api.event.MessageAddedEvent;
|
||||
import org.briarproject.api.event.MessageRequestedEvent;
|
||||
import org.briarproject.api.event.MessageToAckEvent;
|
||||
import org.briarproject.api.event.MessageToRequestEvent;
|
||||
import org.briarproject.api.event.MessageValidatedEvent;
|
||||
import org.briarproject.api.event.MessagesAckedEvent;
|
||||
import org.briarproject.api.event.MessagesSentEvent;
|
||||
import org.briarproject.api.event.SubscriptionAddedEvent;
|
||||
@@ -34,6 +36,7 @@ import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.lifecycle.ShutdownManager;
|
||||
import org.briarproject.api.sync.Ack;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.Message;
|
||||
@@ -56,7 +59,7 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.api.sync.MessagingConstants.GROUP_SALT_LENGTH;
|
||||
import static org.briarproject.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||
import static org.briarproject.db.DatabaseConstants.MAX_OFFERED_MESSAGES;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -66,28 +69,31 @@ import static org.junit.Assert.fail;
|
||||
|
||||
public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
|
||||
protected final Object txn = new Object();
|
||||
protected final GroupId groupId;
|
||||
protected final Group group;
|
||||
protected final AuthorId authorId;
|
||||
protected final Author author;
|
||||
protected final AuthorId localAuthorId;
|
||||
protected final LocalAuthor localAuthor;
|
||||
protected final MessageId messageId, messageId1;
|
||||
protected final String contentType;
|
||||
protected final long timestamp;
|
||||
protected final int size;
|
||||
protected final byte[] raw;
|
||||
protected final Message message, message1;
|
||||
protected final TransportId transportId;
|
||||
protected final TransportProperties transportProperties;
|
||||
protected final int maxLatency;
|
||||
protected final ContactId contactId;
|
||||
protected final Contact contact;
|
||||
private final Object txn = new Object();
|
||||
private final ClientId clientId;
|
||||
private final GroupId groupId;
|
||||
private final Group group;
|
||||
private final AuthorId authorId;
|
||||
private final Author author;
|
||||
private final AuthorId localAuthorId;
|
||||
private final LocalAuthor localAuthor;
|
||||
private final MessageId messageId, messageId1;
|
||||
private final int size;
|
||||
private final byte[] raw;
|
||||
private final Message message;
|
||||
private final Metadata metadata;
|
||||
private final TransportId transportId;
|
||||
private final TransportProperties transportProperties;
|
||||
private final int maxLatency;
|
||||
private final ContactId contactId;
|
||||
private final Contact contact;
|
||||
|
||||
public DatabaseComponentImplTest() {
|
||||
clientId = new ClientId(TestUtils.getRandomId());
|
||||
groupId = new GroupId(TestUtils.getRandomId());
|
||||
group = new Group(groupId, "Group", new byte[GROUP_SALT_LENGTH]);
|
||||
ClientId clientId = new ClientId(TestUtils.getRandomId());
|
||||
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
|
||||
group = new Group(groupId, clientId, descriptor);
|
||||
authorId = new AuthorId(TestUtils.getRandomId());
|
||||
author = new Author(authorId, "Alice", new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||
localAuthorId = new AuthorId(TestUtils.getRandomId());
|
||||
@@ -95,14 +101,12 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100], 1234);
|
||||
messageId = new MessageId(TestUtils.getRandomId());
|
||||
messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
contentType = "text/plain";
|
||||
timestamp = System.currentTimeMillis();
|
||||
long timestamp = System.currentTimeMillis();
|
||||
size = 1234;
|
||||
raw = new byte[size];
|
||||
message = new TestMessage(messageId, null, group, author, contentType,
|
||||
timestamp, raw);
|
||||
message1 = new TestMessage(messageId1, messageId, group, null,
|
||||
contentType, timestamp, raw);
|
||||
message = new Message(messageId, groupId, timestamp, raw);
|
||||
metadata = new Metadata();
|
||||
metadata.put("foo", new byte[] {'b', 'a', 'r'});
|
||||
transportId = new TransportId("id");
|
||||
transportProperties = new TransportProperties(Collections.singletonMap(
|
||||
"bar", "baz"));
|
||||
@@ -125,9 +129,9 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
exactly(11).of(database).startTransaction();
|
||||
exactly(10).of(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
exactly(11).of(database).commitTransaction(txn);
|
||||
exactly(10).of(database).commitTransaction(txn);
|
||||
// open()
|
||||
oneOf(database).open();
|
||||
will(returnValue(false));
|
||||
@@ -161,11 +165,6 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
// addGroup() again
|
||||
oneOf(database).containsGroup(txn, groupId);
|
||||
will(returnValue(true));
|
||||
// getMessageHeaders()
|
||||
oneOf(database).containsGroup(txn, groupId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).getMessageHeaders(txn, groupId);
|
||||
will(returnValue(Collections.emptyList()));
|
||||
// getGroups()
|
||||
oneOf(database).getGroups(txn);
|
||||
will(returnValue(Collections.singletonList(group)));
|
||||
@@ -182,8 +181,6 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
// removeContact()
|
||||
oneOf(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).getInboxGroupId(txn, contactId);
|
||||
will(returnValue(null));
|
||||
oneOf(database).removeContact(txn, contactId);
|
||||
oneOf(eventBus).broadcast(with(any(ContactRemovedEvent.class)));
|
||||
// removeLocalAuthor()
|
||||
@@ -208,7 +205,6 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
db.getRemoteProperties(transportId));
|
||||
db.addGroup(group); // First time - listeners called
|
||||
db.addGroup(group); // Second time - not called
|
||||
assertEquals(Collections.emptyList(), db.getMessageHeaders(groupId));
|
||||
assertEquals(Collections.singletonList(group), db.getGroups());
|
||||
db.removeGroup(group);
|
||||
db.removeContact(contactId);
|
||||
@@ -237,7 +233,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
db.addLocalMessage(message);
|
||||
db.addLocalMessage(message, clientId, metadata);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -262,7 +258,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
db.addLocalMessage(message);
|
||||
db.addLocalMessage(message, clientId, metadata);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -282,7 +278,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
oneOf(database).containsGroup(txn, groupId);
|
||||
will(returnValue(true));
|
||||
oneOf(database).addMessage(txn, message, true);
|
||||
oneOf(database).setReadFlag(txn, messageId, true);
|
||||
oneOf(database).mergeMessageMetadata(txn, messageId, metadata);
|
||||
oneOf(database).getVisibility(txn, groupId);
|
||||
will(returnValue(Collections.singletonList(contactId)));
|
||||
oneOf(database).getContactIds(txn);
|
||||
@@ -291,13 +287,14 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
will(returnValue(false));
|
||||
oneOf(database).addStatus(txn, contactId, messageId, false, false);
|
||||
oneOf(database).commitTransaction(txn);
|
||||
// The message was added, so the listener should be called
|
||||
// The message was added, so the listeners should be called
|
||||
oneOf(eventBus).broadcast(with(any(MessageAddedEvent.class)));
|
||||
oneOf(eventBus).broadcast(with(any(MessageValidatedEvent.class)));
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
db.addLocalMessage(message);
|
||||
db.addLocalMessage(message, clientId, metadata);
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
@@ -385,7 +382,14 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.getInboxGroupId(contactId);
|
||||
db.getMessageStatus(contactId, groupId);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.getMessageStatus(contactId, messageId);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
@@ -469,13 +473,6 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.setInboxGroup(contactId, group);
|
||||
fail();
|
||||
} catch (NoSuchContactException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@@ -540,6 +537,9 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
exactly(5).of(database).containsGroup(txn, groupId);
|
||||
will(returnValue(false));
|
||||
exactly(5).of(database).abortTransaction(txn);
|
||||
// This is needed for getMessageStatus() to proceed
|
||||
exactly(1).of(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
@@ -552,7 +552,7 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
try {
|
||||
db.getMessageHeaders(groupId);
|
||||
db.getMessageStatus(contactId, groupId);
|
||||
fail();
|
||||
} catch (NoSuchSubscriptionException expected) {
|
||||
// Expected
|
||||
@@ -582,6 +582,59 @@ public class DatabaseComponentImplTest extends BriarTestCase {
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVariousMethodsThrowExceptionIfMessageIsMissing()
|
||||
throws Exception {
|
||||
Mockery context = new Mockery();
|
||||
@SuppressWarnings("unchecked")
|
||||
final Database<Object> database = context.mock(Database.class);
|
||||
final ShutdownManager shutdown = context.mock(ShutdownManager.class);
|
||||
final EventBus eventBus = context.mock(EventBus.class);
|
||||
context.checking(new Expectations() {{
|
||||
// Check whether the message is in the DB (which it's not)
|
||||
exactly(4).of(database).startTransaction();
|
||||
will(returnValue(txn));
|
||||
exactly(4).of(database).containsMessage(txn, messageId);
|
||||
will(returnValue(false));
|
||||
exactly(4).of(database).abortTransaction(txn);
|
||||
// This is needed for getMessageStatus() to proceed
|
||||
exactly(1).of(database).containsContact(txn, contactId);
|
||||
will(returnValue(true));
|
||||
}});
|
||||
DatabaseComponent db = createDatabaseComponent(database, eventBus,
|
||||
shutdown);
|
||||
|
||||
try {
|
||||
db.getRawMessage(messageId);
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.getMessageMetadata(messageId);
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.getMessageStatus(contactId, messageId);
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
try {
|
||||
db.mergeMessageMetadata(messageId, metadata);
|
||||
fail();
|
||||
} catch (NoSuchMessageException expected) {
|
||||
// Expected
|
||||
}
|
||||
|
||||
context.assertIsSatisfied();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVariousMethodsThrowExceptionIfTransportIsMissing()
|
||||
throws Exception {
|
||||
|
||||
@@ -2,21 +2,22 @@ package org.briarproject.db;
|
||||
|
||||
import org.briarproject.BriarTestCase;
|
||||
import org.briarproject.TestDatabaseConfig;
|
||||
import org.briarproject.TestMessage;
|
||||
import org.briarproject.TestUtils;
|
||||
import org.briarproject.api.TransportId;
|
||||
import org.briarproject.api.TransportProperties;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.crypto.SecretKey;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Metadata;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
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.MessageHeader;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.api.sync.MessageStatus;
|
||||
import org.briarproject.api.transport.IncomingKeys;
|
||||
import org.briarproject.api.transport.OutgoingKeys;
|
||||
import org.briarproject.api.transport.TransportKeys;
|
||||
@@ -42,13 +43,11 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static java.util.concurrent.TimeUnit.SECONDS;
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.api.sync.MessageHeader.State.STORED;
|
||||
import static org.briarproject.api.sync.MessagingConstants.GROUP_SALT_LENGTH;
|
||||
import static org.briarproject.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||
import static org.briarproject.api.sync.SyncConstants.MAX_MESSAGE_LENGTH;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
@@ -65,7 +64,6 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
private final AuthorId localAuthorId;
|
||||
private final LocalAuthor localAuthor;
|
||||
private final MessageId messageId;
|
||||
private final String contentType;
|
||||
private final long timestamp;
|
||||
private final int size;
|
||||
private final byte[] raw;
|
||||
@@ -75,20 +73,20 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
|
||||
public H2DatabaseTest() throws Exception {
|
||||
groupId = new GroupId(TestUtils.getRandomId());
|
||||
group = new Group(groupId, "Group", new byte[GROUP_SALT_LENGTH]);
|
||||
ClientId clientId = new ClientId(TestUtils.getRandomId());
|
||||
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
|
||||
group = new Group(groupId, clientId, descriptor);
|
||||
AuthorId authorId = new AuthorId(TestUtils.getRandomId());
|
||||
author = new Author(authorId, "Alice", new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||
localAuthorId = new AuthorId(TestUtils.getRandomId());
|
||||
localAuthor = new LocalAuthor(localAuthorId, "Bob",
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100], 1234);
|
||||
messageId = new MessageId(TestUtils.getRandomId());
|
||||
contentType = "text/plain";
|
||||
timestamp = System.currentTimeMillis();
|
||||
size = 1234;
|
||||
raw = new byte[size];
|
||||
random.nextBytes(raw);
|
||||
message = new TestMessage(messageId, null, group, author, contentType,
|
||||
timestamp, raw);
|
||||
message = new Message(messageId, groupId, timestamp, raw);
|
||||
transportId = new TransportId("id");
|
||||
contactId = new ContactId(1);
|
||||
}
|
||||
@@ -309,8 +307,7 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
|
||||
// Add some messages to ack
|
||||
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
Message message1 = new TestMessage(messageId1, null, group, author,
|
||||
contentType, timestamp, raw);
|
||||
Message message1 = new Message(messageId1, groupId, timestamp, raw);
|
||||
db.addMessage(txn, message, true);
|
||||
db.addStatus(txn, contactId, messageId, false, true);
|
||||
db.raiseAckFlag(txn, contactId, messageId);
|
||||
@@ -404,10 +401,9 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testGetFreeSpace() throws Exception {
|
||||
byte[] largeBody = new byte[ONE_MEGABYTE];
|
||||
byte[] largeBody = new byte[MAX_MESSAGE_LENGTH];
|
||||
for (int i = 0; i < largeBody.length; i++) largeBody[i] = (byte) i;
|
||||
Message message = new TestMessage(messageId, null, group, author,
|
||||
contentType, timestamp, largeBody);
|
||||
Message message = new Message(messageId, groupId, timestamp, largeBody);
|
||||
Database<Connection> db = open(false);
|
||||
|
||||
// Sanity check: there should be enough space on disk for this test
|
||||
@@ -718,343 +714,15 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetParentWithNoParent() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Subscribe to a group
|
||||
db.addGroup(txn, group);
|
||||
|
||||
// A message with no parent should return null
|
||||
MessageId childId = new MessageId(TestUtils.getRandomId());
|
||||
Message child = new TestMessage(childId, null, group, null, contentType,
|
||||
timestamp, raw);
|
||||
db.addMessage(txn, child, true);
|
||||
assertTrue(db.containsMessage(txn, childId));
|
||||
assertNull(db.getParent(txn, childId));
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetParentWithAbsentParent() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Subscribe to a group
|
||||
db.addGroup(txn, group);
|
||||
|
||||
// A message with an absent parent should return null
|
||||
MessageId childId = new MessageId(TestUtils.getRandomId());
|
||||
MessageId parentId = new MessageId(TestUtils.getRandomId());
|
||||
Message child = new TestMessage(childId, parentId, group, null,
|
||||
contentType, timestamp, raw);
|
||||
db.addMessage(txn, child, true);
|
||||
assertTrue(db.containsMessage(txn, childId));
|
||||
assertFalse(db.containsMessage(txn, parentId));
|
||||
assertNull(db.getParent(txn, childId));
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetParentWithParentInAnotherGroup() throws Exception {
|
||||
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
|
||||
Group group1 = new Group(groupId1, "Another group",
|
||||
new byte[GROUP_SALT_LENGTH]);
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Subscribe to two groups
|
||||
db.addGroup(txn, group);
|
||||
db.addGroup(txn, group1);
|
||||
|
||||
// A message with a parent in another group should return null
|
||||
MessageId childId = new MessageId(TestUtils.getRandomId());
|
||||
MessageId parentId = new MessageId(TestUtils.getRandomId());
|
||||
Message child = new TestMessage(childId, parentId, group, null,
|
||||
contentType, timestamp, raw);
|
||||
Message parent = new TestMessage(parentId, null, group1, null,
|
||||
contentType, timestamp, raw);
|
||||
db.addMessage(txn, child, true);
|
||||
db.addMessage(txn, parent, true);
|
||||
assertTrue(db.containsMessage(txn, childId));
|
||||
assertTrue(db.containsMessage(txn, parentId));
|
||||
assertNull(db.getParent(txn, childId));
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetParentWithParentInSameGroup() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Subscribe to a group
|
||||
db.addGroup(txn, group);
|
||||
|
||||
// A message with a parent in the same group should return the parent
|
||||
MessageId childId = new MessageId(TestUtils.getRandomId());
|
||||
MessageId parentId = new MessageId(TestUtils.getRandomId());
|
||||
Message child = new TestMessage(childId, parentId, group, null,
|
||||
contentType, timestamp, raw);
|
||||
Message parent = new TestMessage(parentId, null, group, null,
|
||||
contentType, timestamp, raw);
|
||||
db.addMessage(txn, child, true);
|
||||
db.addMessage(txn, parent, true);
|
||||
assertTrue(db.containsMessage(txn, childId));
|
||||
assertTrue(db.containsMessage(txn, parentId));
|
||||
assertEquals(parentId, db.getParent(txn, childId));
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMessageBody() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Add a contact and subscribe to a group
|
||||
db.addLocalAuthor(txn, localAuthor);
|
||||
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
||||
db.addGroup(txn, group);
|
||||
|
||||
// Store a couple of messages
|
||||
int bodyLength = raw.length - 20;
|
||||
Message message = new TestMessage(messageId, null, group, null,
|
||||
contentType, timestamp, raw, 5, bodyLength);
|
||||
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
Message message1 = new TestMessage(messageId1, null, group, null,
|
||||
contentType, timestamp, raw, 10, bodyLength);
|
||||
db.addMessage(txn, message, true);
|
||||
db.addMessage(txn, message1, true);
|
||||
|
||||
// Calculate the expected message bodies
|
||||
byte[] expectedBody = new byte[bodyLength];
|
||||
System.arraycopy(raw, 5, expectedBody, 0, bodyLength);
|
||||
assertFalse(Arrays.equals(expectedBody, new byte[bodyLength]));
|
||||
byte[] expectedBody1 = new byte[bodyLength];
|
||||
System.arraycopy(raw, 10, expectedBody1, 0, bodyLength);
|
||||
System.arraycopy(raw, 10, expectedBody1, 0, bodyLength);
|
||||
|
||||
// Retrieve the raw messages
|
||||
assertArrayEquals(raw, db.getRawMessage(txn, messageId));
|
||||
assertArrayEquals(raw, db.getRawMessage(txn, messageId1));
|
||||
|
||||
// Retrieve the message bodies
|
||||
byte[] body = db.getMessageBody(txn, messageId);
|
||||
assertArrayEquals(expectedBody, body);
|
||||
byte[] body1 = db.getMessageBody(txn, messageId1);
|
||||
assertArrayEquals(expectedBody1, body1);
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMessageHeaders() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Subscribe to a group
|
||||
db.addGroup(txn, group);
|
||||
|
||||
// Store a couple of messages
|
||||
db.addMessage(txn, message, true);
|
||||
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
MessageId parentId = new MessageId(TestUtils.getRandomId());
|
||||
long timestamp1 = System.currentTimeMillis();
|
||||
Message message1 = new TestMessage(messageId1, parentId, group, author,
|
||||
contentType, timestamp1, raw);
|
||||
db.addMessage(txn, message1, true);
|
||||
// Mark one of the messages read
|
||||
db.setReadFlag(txn, messageId, true);
|
||||
|
||||
// Retrieve the message headers (order is undefined)
|
||||
Collection<MessageHeader> headers = db.getMessageHeaders(txn, groupId);
|
||||
assertEquals(2, headers.size());
|
||||
boolean firstFound = false, secondFound = false;
|
||||
for (MessageHeader header : headers) {
|
||||
if (messageId.equals(header.getId())) {
|
||||
assertHeadersMatch(message, header);
|
||||
assertTrue(header.isRead());
|
||||
firstFound = true;
|
||||
} else if (messageId1.equals(header.getId())) {
|
||||
assertHeadersMatch(message1, header);
|
||||
assertFalse(header.isRead());
|
||||
secondFound = true;
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
// Both the headers should have been retrieved
|
||||
assertTrue(firstFound);
|
||||
assertTrue(secondFound);
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
private void assertHeadersMatch(Message m, MessageHeader h) {
|
||||
assertEquals(m.getId(), h.getId());
|
||||
if (m.getParent() == null) assertNull(h.getParent());
|
||||
else assertEquals(m.getParent(), h.getParent());
|
||||
assertEquals(m.getGroup().getId(), h.getGroupId());
|
||||
if (m.getAuthor() == null) assertNull(h.getAuthor());
|
||||
else assertEquals(m.getAuthor(), h.getAuthor());
|
||||
assertEquals(m.getContentType(), h.getContentType());
|
||||
assertEquals(m.getTimestamp(), h.getTimestamp());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAuthorStatus() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Add a contact and subscribe to a group
|
||||
db.addLocalAuthor(txn, localAuthor);
|
||||
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
||||
db.addGroup(txn, group);
|
||||
|
||||
// Store a message from the contact - status VERIFIED
|
||||
db.addMessage(txn, message, true);
|
||||
AuthorId authorId1 = new AuthorId(TestUtils.getRandomId());
|
||||
// Store a message from an unknown author - status UNKNOWN
|
||||
Author author1 = new Author(authorId1, "Bob",
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
Message message1 = new TestMessage(messageId1, null, group, author1,
|
||||
contentType, timestamp, raw);
|
||||
db.addMessage(txn, message1, true);
|
||||
// Store an anonymous message - status ANONYMOUS
|
||||
MessageId messageId2 = new MessageId(TestUtils.getRandomId());
|
||||
Message message2 = new TestMessage(messageId2, null, group, null,
|
||||
contentType, timestamp, raw);
|
||||
db.addMessage(txn, message2, true);
|
||||
|
||||
// Retrieve the message headers (order is undefined)
|
||||
Collection<MessageHeader> headers = db.getMessageHeaders(txn, groupId);
|
||||
assertEquals(3, headers.size());
|
||||
boolean firstFound = false, secondFound = false, thirdFound = false;
|
||||
for (MessageHeader header : headers) {
|
||||
if (messageId.equals(header.getId())) {
|
||||
assertHeadersMatch(message, header);
|
||||
assertEquals(Author.Status.VERIFIED, header.getAuthorStatus());
|
||||
firstFound = true;
|
||||
} else if (messageId1.equals(header.getId())) {
|
||||
assertHeadersMatch(message1, header);
|
||||
assertEquals(Author.Status.UNKNOWN, header.getAuthorStatus());
|
||||
secondFound = true;
|
||||
} else if (messageId2.equals(header.getId())) {
|
||||
assertHeadersMatch(message2, header);
|
||||
assertEquals(Author.Status.ANONYMOUS, header.getAuthorStatus());
|
||||
thirdFound = true;
|
||||
} else {
|
||||
fail();
|
||||
}
|
||||
}
|
||||
// All of the headers should have been retrieved
|
||||
assertTrue(firstFound);
|
||||
assertTrue(secondFound);
|
||||
assertTrue(thirdFound);
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadFlag() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Subscribe to a group and store a message
|
||||
db.addGroup(txn, group);
|
||||
db.addMessage(txn, message, true);
|
||||
|
||||
// The message should be unread by default
|
||||
assertFalse(db.getReadFlag(txn, messageId));
|
||||
// Mark the message read
|
||||
db.setReadFlag(txn, messageId, true);
|
||||
// The message should be read
|
||||
assertTrue(db.getReadFlag(txn, messageId));
|
||||
// Mark the message unread
|
||||
db.setReadFlag(txn, messageId, false);
|
||||
// The message should be unread
|
||||
assertFalse(db.getReadFlag(txn, messageId));
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetUnreadMessageCounts() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Subscribe to a couple of groups
|
||||
db.addGroup(txn, group);
|
||||
GroupId groupId1 = new GroupId(TestUtils.getRandomId());
|
||||
Group group1 = new Group(groupId1, "Another group",
|
||||
new byte[GROUP_SALT_LENGTH]);
|
||||
db.addGroup(txn, group1);
|
||||
|
||||
// Store two messages in the first group
|
||||
db.addMessage(txn, message, true);
|
||||
MessageId messageId1 = new MessageId(TestUtils.getRandomId());
|
||||
Message message1 = new TestMessage(messageId1, null, group, author,
|
||||
contentType, timestamp, raw);
|
||||
db.addMessage(txn, message1, true);
|
||||
|
||||
// Store one message in the second group
|
||||
MessageId messageId2 = new MessageId(TestUtils.getRandomId());
|
||||
Message message2 = new TestMessage(messageId2, null, group1, author,
|
||||
contentType, timestamp, raw);
|
||||
db.addMessage(txn, message2, true);
|
||||
|
||||
// Mark one of the messages in the first group read
|
||||
db.setReadFlag(txn, messageId, true);
|
||||
|
||||
// There should be one unread message in each group
|
||||
Map<GroupId, Integer> counts = db.getUnreadMessageCounts(txn);
|
||||
assertEquals(2, counts.size());
|
||||
Integer count = counts.get(groupId);
|
||||
assertNotNull(count);
|
||||
assertEquals(1, count.intValue());
|
||||
count = counts.get(groupId1);
|
||||
assertNotNull(count);
|
||||
assertEquals(1, count.intValue());
|
||||
|
||||
// Mark the read message unread
|
||||
db.setReadFlag(txn, messageId, false);
|
||||
|
||||
// Mark the message in the second group read
|
||||
db.setReadFlag(txn, messageId2, true);
|
||||
|
||||
// There should be two unread messages in the first group, none in
|
||||
// the second group
|
||||
counts = db.getUnreadMessageCounts(txn);
|
||||
assertEquals(1, counts.size());
|
||||
count = counts.get(groupId);
|
||||
assertNotNull(count);
|
||||
assertEquals(2, count.intValue());
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleSubscriptionsAndUnsubscriptions() throws Exception {
|
||||
// Create some groups
|
||||
List<Group> groups = new ArrayList<Group>();
|
||||
for (int i = 0; i < 100; i++) {
|
||||
GroupId id = new GroupId(TestUtils.getRandomId());
|
||||
String name = "Group " + i;
|
||||
groups.add(new Group(id, name, new byte[GROUP_SALT_LENGTH]));
|
||||
ClientId clientId = new ClientId(TestUtils.getRandomId());
|
||||
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
|
||||
groups.add(new Group(id, clientId, descriptor));
|
||||
}
|
||||
|
||||
Database<Connection> db = open(false);
|
||||
@@ -1288,41 +956,6 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetInboxMessageHeaders() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Add a contact and an inbox group - no headers should be returned
|
||||
db.addLocalAuthor(txn, localAuthor);
|
||||
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
||||
db.addGroup(txn, group);
|
||||
db.setInboxGroup(txn, contactId, group);
|
||||
assertEquals(Collections.emptyList(),
|
||||
db.getInboxMessageHeaders(txn, contactId));
|
||||
|
||||
// Add a message to the inbox group - the header should be returned
|
||||
db.addMessage(txn, message, true);
|
||||
db.addStatus(txn, contactId, messageId, false, false);
|
||||
Collection<MessageHeader> headers =
|
||||
db.getInboxMessageHeaders(txn, contactId);
|
||||
assertEquals(1, headers.size());
|
||||
MessageHeader header = headers.iterator().next();
|
||||
assertEquals(messageId, header.getId());
|
||||
assertNull(header.getParent());
|
||||
assertEquals(groupId, header.getGroupId());
|
||||
assertEquals(localAuthor, header.getAuthor());
|
||||
assertEquals(contentType, header.getContentType());
|
||||
assertEquals(timestamp, header.getTimestamp());
|
||||
assertEquals(true, header.isLocal());
|
||||
assertEquals(false, header.isRead());
|
||||
assertEquals(STORED, header.getStatus());
|
||||
assertFalse(header.isRead());
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOfferedMessages() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
@@ -1392,6 +1025,116 @@ public class H2DatabaseTest extends BriarTestCase {
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageMetadata() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Add a group and a message
|
||||
db.addGroup(txn, group);
|
||||
db.addMessage(txn, message, true);
|
||||
|
||||
// Attach some metadata to the message
|
||||
Metadata metadata = new Metadata();
|
||||
metadata.put("foo", new byte[]{'b', 'a', 'r'});
|
||||
db.mergeMessageMetadata(txn, messageId, metadata);
|
||||
|
||||
// Retrieve the metadata for the message
|
||||
Metadata retrieved = db.getMessageMetadata(txn, messageId);
|
||||
assertEquals(1, retrieved.size());
|
||||
assertTrue(retrieved.containsKey("foo"));
|
||||
assertArrayEquals(metadata.get("foo"), retrieved.get("foo"));
|
||||
|
||||
// Retrieve the metadata for the group
|
||||
Map<MessageId, Metadata> all = db.getMessageMetadata(txn, groupId);
|
||||
assertEquals(1, all.size());
|
||||
assertTrue(all.containsKey(messageId));
|
||||
retrieved = all.get(messageId);
|
||||
assertEquals(1, retrieved.size());
|
||||
assertTrue(retrieved.containsKey("foo"));
|
||||
assertArrayEquals(metadata.get("foo"), retrieved.get("foo"));
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMessageStatus() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
Connection txn = db.startTransaction();
|
||||
|
||||
// Add a contact who subscribes to a group
|
||||
db.addLocalAuthor(txn, localAuthor);
|
||||
assertEquals(contactId, db.addContact(txn, author, localAuthorId));
|
||||
db.setGroups(txn, contactId, Collections.singletonList(group), 1);
|
||||
|
||||
// Subscribe to the group and make it visible to the contact
|
||||
db.addGroup(txn, group);
|
||||
db.addVisibility(txn, contactId, groupId);
|
||||
|
||||
// Add a message to the group
|
||||
db.addMessage(txn, message, true);
|
||||
db.addStatus(txn, contactId, messageId, false, false);
|
||||
|
||||
// The message should not be sent or seen
|
||||
MessageStatus status = db.getMessageStatus(txn, contactId, messageId);
|
||||
assertEquals(messageId, status.getMessageId());
|
||||
assertEquals(contactId, status.getContactId());
|
||||
assertFalse(status.isSent());
|
||||
assertFalse(status.isSeen());
|
||||
|
||||
// The same status should be returned when querying by group
|
||||
Collection<MessageStatus> statuses = db.getMessageStatus(txn,
|
||||
contactId, groupId);
|
||||
assertEquals(1, statuses.size());
|
||||
status = statuses.iterator().next();
|
||||
assertEquals(messageId, status.getMessageId());
|
||||
assertEquals(contactId, status.getContactId());
|
||||
assertFalse(status.isSent());
|
||||
assertFalse(status.isSeen());
|
||||
|
||||
// Pretend the message was sent to the contact
|
||||
db.updateExpiryTime(txn, contactId, messageId, Integer.MAX_VALUE);
|
||||
|
||||
// The message should be sent but not seen
|
||||
status = db.getMessageStatus(txn, contactId, messageId);
|
||||
assertEquals(messageId, status.getMessageId());
|
||||
assertEquals(contactId, status.getContactId());
|
||||
assertTrue(status.isSent());
|
||||
assertFalse(status.isSeen());
|
||||
|
||||
// The same status should be returned when querying by group
|
||||
statuses = db.getMessageStatus(txn, contactId, groupId);
|
||||
assertEquals(1, statuses.size());
|
||||
status = statuses.iterator().next();
|
||||
assertEquals(messageId, status.getMessageId());
|
||||
assertEquals(contactId, status.getContactId());
|
||||
assertTrue(status.isSent());
|
||||
assertFalse(status.isSeen());
|
||||
|
||||
// Pretend the message was acked by the contact
|
||||
db.raiseSeenFlag(txn, contactId, messageId);
|
||||
|
||||
// The message should be sent and seen
|
||||
status = db.getMessageStatus(txn, contactId, messageId);
|
||||
assertEquals(messageId, status.getMessageId());
|
||||
assertEquals(contactId, status.getContactId());
|
||||
assertTrue(status.isSent());
|
||||
assertTrue(status.isSeen());
|
||||
|
||||
// The same status should be returned when querying by group
|
||||
statuses = db.getMessageStatus(txn, contactId, groupId);
|
||||
assertEquals(1, statuses.size());
|
||||
status = statuses.iterator().next();
|
||||
assertEquals(messageId, status.getMessageId());
|
||||
assertEquals(contactId, status.getContactId());
|
||||
assertTrue(status.isSent());
|
||||
assertTrue(status.isSeen());
|
||||
|
||||
db.commitTransaction(txn);
|
||||
db.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExceptionHandling() throws Exception {
|
||||
Database<Connection> db = open(false);
|
||||
|
||||
@@ -15,13 +15,19 @@ import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.crypto.KeyPair;
|
||||
import org.briarproject.api.crypto.PrivateKey;
|
||||
import org.briarproject.api.crypto.Signature;
|
||||
import org.briarproject.api.forum.ForumConstants;
|
||||
import org.briarproject.api.forum.ForumPost;
|
||||
import org.briarproject.api.forum.ForumPostFactory;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.messaging.MessagingConstants;
|
||||
import org.briarproject.api.messaging.PrivateMessage;
|
||||
import org.briarproject.api.messaging.PrivateMessageFactory;
|
||||
import org.briarproject.api.sync.Ack;
|
||||
import org.briarproject.api.sync.ClientId;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.GroupFactory;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.sync.MessageFactory;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.api.sync.Offer;
|
||||
import org.briarproject.api.sync.PacketWriter;
|
||||
@@ -33,6 +39,8 @@ import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.data.DataModule;
|
||||
import org.briarproject.db.DatabaseModule;
|
||||
import org.briarproject.event.EventModule;
|
||||
import org.briarproject.forum.ForumModule;
|
||||
import org.briarproject.messaging.MessagingModule;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
@@ -43,14 +51,14 @@ import java.util.Random;
|
||||
import static org.briarproject.api.TransportPropertyConstants.MAX_PROPERTIES_PER_TRANSPORT;
|
||||
import static org.briarproject.api.TransportPropertyConstants.MAX_PROPERTY_LENGTH;
|
||||
import static org.briarproject.api.TransportPropertyConstants.MAX_TRANSPORT_ID_LENGTH;
|
||||
import static org.briarproject.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
|
||||
import static org.briarproject.api.sync.MessagingConstants.MAX_BODY_LENGTH;
|
||||
import static org.briarproject.api.sync.MessagingConstants.MAX_CONTENT_TYPE_LENGTH;
|
||||
import static org.briarproject.api.sync.MessagingConstants.MAX_GROUP_NAME_LENGTH;
|
||||
import static org.briarproject.api.sync.MessagingConstants.MAX_PAYLOAD_LENGTH;
|
||||
import static org.briarproject.api.sync.MessagingConstants.MAX_SUBSCRIPTIONS;
|
||||
import static org.briarproject.api.messaging.MessagingConstants.MAX_PRIVATE_MESSAGE_BODY_LENGTH;
|
||||
import static org.briarproject.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||
import static org.briarproject.api.sync.SyncConstants.MAX_PACKET_PAYLOAD_LENGTH;
|
||||
import static org.briarproject.api.sync.SyncConstants.MAX_SUBSCRIPTIONS;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ConstantsTest extends BriarTestCase {
|
||||
@@ -58,18 +66,21 @@ public class ConstantsTest extends BriarTestCase {
|
||||
private final CryptoComponent crypto;
|
||||
private final GroupFactory groupFactory;
|
||||
private final AuthorFactory authorFactory;
|
||||
private final MessageFactory messageFactory;
|
||||
private final PrivateMessageFactory privateMessageFactory;
|
||||
private final ForumPostFactory forumPostFactory;
|
||||
private final PacketWriterFactory packetWriterFactory;
|
||||
|
||||
public ConstantsTest() throws Exception {
|
||||
Injector i = Guice.createInjector(new TestDatabaseModule(),
|
||||
new TestLifecycleModule(), new TestSystemModule(),
|
||||
new CryptoModule(), new DatabaseModule(), new EventModule(),
|
||||
new SyncModule(), new DataModule());
|
||||
new CryptoModule(), new DatabaseModule(), new DataModule(),
|
||||
new EventModule(), new ForumModule(), new MessagingModule(),
|
||||
new SyncModule());
|
||||
crypto = i.getInstance(CryptoComponent.class);
|
||||
groupFactory = i.getInstance(GroupFactory.class);
|
||||
authorFactory = i.getInstance(AuthorFactory.class);
|
||||
messageFactory = i.getInstance(MessageFactory.class);
|
||||
privateMessageFactory = i.getInstance(PrivateMessageFactory.class);
|
||||
forumPostFactory = i.getInstance(ForumPostFactory.class);
|
||||
packetWriterFactory = i.getInstance(PacketWriterFactory.class);
|
||||
}
|
||||
|
||||
@@ -100,14 +111,13 @@ public class ConstantsTest extends BriarTestCase {
|
||||
sig.initSign(keyPair.getPrivate());
|
||||
sig.update(toBeSigned);
|
||||
byte[] signature = sig.sign();
|
||||
assertTrue("Length " + signature.length,
|
||||
signature.length <= MAX_SIGNATURE_LENGTH);
|
||||
assertTrue(signature.length <= MAX_SIGNATURE_LENGTH);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageIdsFitIntoLargeAck() throws Exception {
|
||||
testMessageIdsFitIntoAck(MAX_PAYLOAD_LENGTH);
|
||||
testMessageIdsFitIntoAck(MAX_PACKET_PAYLOAD_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -116,36 +126,53 @@ public class ConstantsTest extends BriarTestCase {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageFitsIntoPacket() throws Exception {
|
||||
public void testPrivateMessageFitsIntoPacket() throws Exception {
|
||||
// Create a maximum-length private message
|
||||
GroupId groupId = new GroupId(TestUtils.getRandomId());
|
||||
long timestamp = Long.MAX_VALUE;
|
||||
MessageId parent = new MessageId(TestUtils.getRandomId());
|
||||
// Create a maximum-length group
|
||||
String groupName = TestUtils.createRandomString(MAX_GROUP_NAME_LENGTH);
|
||||
Group group = groupFactory.createGroup(groupName);
|
||||
String contentType = TestUtils.createRandomString(
|
||||
MessagingConstants.MAX_CONTENT_TYPE_LENGTH);
|
||||
byte[] body = new byte[MAX_PRIVATE_MESSAGE_BODY_LENGTH];
|
||||
PrivateMessage message = privateMessageFactory.createPrivateMessage(
|
||||
groupId, timestamp, parent, contentType, body);
|
||||
// Check the size of the serialised message
|
||||
int length = message.getMessage().getRaw().length;
|
||||
assertTrue(length > UniqueId.LENGTH + 8 + UniqueId.LENGTH
|
||||
+ MessagingConstants.MAX_CONTENT_TYPE_LENGTH
|
||||
+ MAX_PRIVATE_MESSAGE_BODY_LENGTH);
|
||||
assertTrue(length <= MAX_PACKET_PAYLOAD_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForumPostFitsIntoPacket() throws Exception {
|
||||
// Create a maximum-length author
|
||||
String authorName =
|
||||
TestUtils.createRandomString(MAX_AUTHOR_NAME_LENGTH);
|
||||
String authorName = TestUtils.createRandomString(
|
||||
MAX_AUTHOR_NAME_LENGTH);
|
||||
byte[] authorPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
|
||||
Author author = authorFactory.createAuthor(authorName, authorPublic);
|
||||
// Create a maximum-length message
|
||||
PrivateKey privateKey = crypto.generateSignatureKeyPair().getPrivate();
|
||||
String contentType =
|
||||
TestUtils.createRandomString(MAX_CONTENT_TYPE_LENGTH);
|
||||
// Create a maximum-length forum post
|
||||
GroupId groupId = new GroupId(TestUtils.getRandomId());
|
||||
long timestamp = Long.MAX_VALUE;
|
||||
byte[] body = new byte[MAX_BODY_LENGTH];
|
||||
Message message = messageFactory.createPseudonymousMessage(parent,
|
||||
group, author, privateKey, contentType, timestamp, body);
|
||||
MessageId parent = new MessageId(TestUtils.getRandomId());
|
||||
String contentType = TestUtils.createRandomString(
|
||||
ForumConstants.MAX_CONTENT_TYPE_LENGTH);
|
||||
byte[] body = new byte[MAX_FORUM_POST_BODY_LENGTH];
|
||||
PrivateKey privateKey = crypto.generateSignatureKeyPair().getPrivate();
|
||||
ForumPost post = forumPostFactory.createPseudonymousPost(groupId,
|
||||
timestamp, parent, author, contentType, body, privateKey);
|
||||
// Check the size of the serialised message
|
||||
int length = message.getSerialised().length;
|
||||
assertTrue(length > UniqueId.LENGTH + MAX_GROUP_NAME_LENGTH
|
||||
+ MAX_PUBLIC_KEY_LENGTH + MAX_AUTHOR_NAME_LENGTH
|
||||
+ MAX_PUBLIC_KEY_LENGTH + MAX_CONTENT_TYPE_LENGTH
|
||||
+ MAX_BODY_LENGTH);
|
||||
assertTrue(length <= MAX_PAYLOAD_LENGTH);
|
||||
int length = post.getMessage().getRaw().length;
|
||||
assertTrue(length > UniqueId.LENGTH + 8 + UniqueId.LENGTH
|
||||
+ MAX_AUTHOR_NAME_LENGTH + MAX_PUBLIC_KEY_LENGTH
|
||||
+ ForumConstants.MAX_CONTENT_TYPE_LENGTH
|
||||
+ MAX_FORUM_POST_BODY_LENGTH);
|
||||
assertTrue(length <= MAX_PACKET_PAYLOAD_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageIdsFitIntoLargeOffer() throws Exception {
|
||||
testMessageIdsFitIntoOffer(MAX_PAYLOAD_LENGTH);
|
||||
testMessageIdsFitIntoOffer(MAX_PACKET_PAYLOAD_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -155,7 +182,7 @@ public class ConstantsTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testMessageIdsFitIntoLargeRequest() throws Exception {
|
||||
testMessageIdsFitIntoRequest(MAX_PAYLOAD_LENGTH);
|
||||
testMessageIdsFitIntoRequest(MAX_PACKET_PAYLOAD_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -181,16 +208,19 @@ public class ConstantsTest extends BriarTestCase {
|
||||
PacketWriter writer = packetWriterFactory.createPacketWriter(out);
|
||||
writer.writeTransportUpdate(u);
|
||||
// Check the size of the serialised transport update
|
||||
assertTrue(out.size() <= MAX_PAYLOAD_LENGTH);
|
||||
assertTrue(out.size() <= MAX_PACKET_PAYLOAD_LENGTH);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroupsFitIntoSubscriptionUpdate() throws Exception {
|
||||
// Create the maximum number of maximum-length groups
|
||||
Random random = new Random();
|
||||
ClientId clientId = new ClientId(TestUtils.getRandomId());
|
||||
Collection<Group> groups = new ArrayList<Group>();
|
||||
for (int i = 0; i < MAX_SUBSCRIPTIONS; i++) {
|
||||
String name = TestUtils.createRandomString(MAX_GROUP_NAME_LENGTH);
|
||||
groups.add(groupFactory.createGroup(name));
|
||||
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
|
||||
random.nextBytes(descriptor);
|
||||
groups.add(groupFactory.createGroup(clientId, descriptor));
|
||||
}
|
||||
// Create a maximum-length subscription update
|
||||
SubscriptionUpdate u = new SubscriptionUpdate(groups, Long.MAX_VALUE);
|
||||
@@ -199,7 +229,7 @@ public class ConstantsTest extends BriarTestCase {
|
||||
PacketWriter writer = packetWriterFactory.createPacketWriter(out);
|
||||
writer.writeSubscriptionUpdate(u);
|
||||
// Check the size of the serialised subscription update
|
||||
assertTrue(out.size() <= MAX_PAYLOAD_LENGTH);
|
||||
assertTrue(out.size() <= MAX_PACKET_PAYLOAD_LENGTH);
|
||||
}
|
||||
|
||||
private void testMessageIdsFitIntoAck(int length) throws Exception {
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
package org.briarproject.sync;
|
||||
|
||||
import org.briarproject.BriarTestCase;
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.crypto.MessageDigest;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.security.GeneralSecurityException;
|
||||
import java.util.Random;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class ConsumersTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testDigestingConsumer() throws Exception {
|
||||
byte[] data = new byte[1234];
|
||||
// Generate some random data and digest it
|
||||
new Random().nextBytes(data);
|
||||
MessageDigest messageDigest = new TestMessageDigest();
|
||||
messageDigest.update(data);
|
||||
byte[] dig = messageDigest.digest();
|
||||
// Check that feeding a DigestingConsumer generates the same digest
|
||||
DigestingConsumer dc = new DigestingConsumer(messageDigest);
|
||||
dc.write(data[0]);
|
||||
dc.write(data, 1, data.length - 2);
|
||||
dc.write(data[data.length - 1]);
|
||||
byte[] dig1 = messageDigest.digest();
|
||||
assertArrayEquals(dig, dig1);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testCountingConsumer() throws Exception {
|
||||
byte[] data = new byte[1234];
|
||||
CountingConsumer cc = new CountingConsumer(data.length);
|
||||
cc.write(data[0]);
|
||||
cc.write(data, 1, data.length - 2);
|
||||
cc.write(data[data.length - 1]);
|
||||
assertEquals(data.length, cc.getCount());
|
||||
cc.write((byte) 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCopyingConsumer() throws Exception {
|
||||
byte[] data = new byte[1234];
|
||||
new Random().nextBytes(data);
|
||||
// Check that a CopyingConsumer creates a faithful copy
|
||||
CopyingConsumer cc = new CopyingConsumer();
|
||||
cc.write(data[0]);
|
||||
cc.write(data, 1, data.length - 2);
|
||||
cc.write(data[data.length - 1]);
|
||||
assertArrayEquals(data, cc.getCopy());
|
||||
}
|
||||
|
||||
private static class TestMessageDigest implements MessageDigest {
|
||||
|
||||
private final java.security.MessageDigest delegate;
|
||||
|
||||
private TestMessageDigest() throws GeneralSecurityException {
|
||||
delegate = java.security.MessageDigest.getInstance("SHA-256");
|
||||
}
|
||||
|
||||
public byte[] digest() {
|
||||
return delegate.digest();
|
||||
}
|
||||
|
||||
public byte[] digest(byte[] input) {
|
||||
return delegate.digest(input);
|
||||
}
|
||||
|
||||
public int digest(byte[] buf, int offset, int len) {
|
||||
byte[] digest = digest();
|
||||
len = Math.min(len, digest.length);
|
||||
System.arraycopy(digest, 0, buf, offset, len);
|
||||
return len;
|
||||
}
|
||||
|
||||
public int getDigestLength() {
|
||||
return delegate.getDigestLength();
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
delegate.reset();
|
||||
}
|
||||
|
||||
public void update(byte input) {
|
||||
delegate.update(input);
|
||||
}
|
||||
|
||||
public void update(byte[] input) {
|
||||
delegate.update(input);
|
||||
}
|
||||
|
||||
public void update(byte[] input, int offset, int len) {
|
||||
delegate.update(input, offset, len);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,50 +1,29 @@
|
||||
package org.briarproject.sync;
|
||||
|
||||
import com.google.inject.Guice;
|
||||
import com.google.inject.Injector;
|
||||
|
||||
import org.briarproject.BriarTestCase;
|
||||
import org.briarproject.TestUtils;
|
||||
import org.briarproject.api.FormatException;
|
||||
import org.briarproject.api.data.BdfReaderFactory;
|
||||
import org.briarproject.api.data.BdfWriter;
|
||||
import org.briarproject.api.data.BdfWriterFactory;
|
||||
import org.briarproject.data.DataModule;
|
||||
import org.briarproject.api.UniqueId;
|
||||
import org.briarproject.util.ByteUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
import static org.briarproject.api.data.DataConstants.LIST_END_LENGTH;
|
||||
import static org.briarproject.api.data.DataConstants.UNIQUE_ID_LENGTH;
|
||||
import static org.briarproject.api.sync.MessagingConstants.HEADER_LENGTH;
|
||||
import static org.briarproject.api.sync.MessagingConstants.MAX_PAYLOAD_LENGTH;
|
||||
import static org.briarproject.api.sync.PacketTypes.ACK;
|
||||
import static org.briarproject.api.sync.PacketTypes.OFFER;
|
||||
import static org.briarproject.api.sync.PacketTypes.REQUEST;
|
||||
import static org.briarproject.api.sync.SyncConstants.MAX_PACKET_PAYLOAD_LENGTH;
|
||||
import static org.briarproject.api.sync.SyncConstants.PACKET_HEADER_LENGTH;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
public class PacketReaderImplTest extends BriarTestCase {
|
||||
|
||||
// FIXME: This is an integration test, not a unit test
|
||||
|
||||
private final BdfReaderFactory bdfReaderFactory;
|
||||
private final BdfWriterFactory bdfWriterFactory;
|
||||
|
||||
public PacketReaderImplTest() throws Exception {
|
||||
Injector i = Guice.createInjector(new DataModule());
|
||||
bdfReaderFactory = i.getInstance(BdfReaderFactory.class);
|
||||
bdfWriterFactory = i.getInstance(BdfWriterFactory.class);
|
||||
}
|
||||
|
||||
@Test(expected = FormatException.class)
|
||||
public void testFormatExceptionIfAckIsTooLarge() throws Exception {
|
||||
byte[] b = createAck(true);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(bdfReaderFactory, null,
|
||||
null, in);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
|
||||
reader.readAck();
|
||||
}
|
||||
|
||||
@@ -52,8 +31,7 @@ public class PacketReaderImplTest extends BriarTestCase {
|
||||
public void testNoFormatExceptionIfAckIsMaximumSize() throws Exception {
|
||||
byte[] b = createAck(false);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(bdfReaderFactory, null,
|
||||
null, in);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
|
||||
reader.readAck();
|
||||
}
|
||||
|
||||
@@ -61,8 +39,7 @@ public class PacketReaderImplTest extends BriarTestCase {
|
||||
public void testEmptyAck() throws Exception {
|
||||
byte[] b = createEmptyAck();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(bdfReaderFactory, null,
|
||||
null, in);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
|
||||
reader.readAck();
|
||||
}
|
||||
|
||||
@@ -70,8 +47,7 @@ public class PacketReaderImplTest extends BriarTestCase {
|
||||
public void testFormatExceptionIfOfferIsTooLarge() throws Exception {
|
||||
byte[] b = createOffer(true);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(bdfReaderFactory, null,
|
||||
null, in);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
|
||||
reader.readOffer();
|
||||
}
|
||||
|
||||
@@ -79,10 +55,7 @@ public class PacketReaderImplTest extends BriarTestCase {
|
||||
public void testNoFormatExceptionIfOfferIsMaximumSize() throws Exception {
|
||||
byte[] b = createOffer(false);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
PacketReaderImpl
|
||||
reader = new PacketReaderImpl(
|
||||
bdfReaderFactory, null,
|
||||
null, in);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
|
||||
reader.readOffer();
|
||||
}
|
||||
|
||||
@@ -90,8 +63,7 @@ public class PacketReaderImplTest extends BriarTestCase {
|
||||
public void testEmptyOffer() throws Exception {
|
||||
byte[] b = createEmptyOffer();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(bdfReaderFactory, null,
|
||||
null, in);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
|
||||
reader.readOffer();
|
||||
}
|
||||
|
||||
@@ -99,8 +71,7 @@ public class PacketReaderImplTest extends BriarTestCase {
|
||||
public void testFormatExceptionIfRequestIsTooLarge() throws Exception {
|
||||
byte[] b = createRequest(true);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(bdfReaderFactory, null,
|
||||
null, in);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
|
||||
reader.readRequest();
|
||||
}
|
||||
|
||||
@@ -108,8 +79,7 @@ public class PacketReaderImplTest extends BriarTestCase {
|
||||
public void testNoFormatExceptionIfRequestIsMaximumSize() throws Exception {
|
||||
byte[] b = createRequest(false);
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(bdfReaderFactory, null,
|
||||
null, in);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
|
||||
reader.readRequest();
|
||||
}
|
||||
|
||||
@@ -117,110 +87,76 @@ public class PacketReaderImplTest extends BriarTestCase {
|
||||
public void testEmptyRequest() throws Exception {
|
||||
byte[] b = createEmptyRequest();
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(b);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(bdfReaderFactory, null,
|
||||
null, in);
|
||||
PacketReaderImpl reader = new PacketReaderImpl(null, null, null, in);
|
||||
reader.readRequest();
|
||||
}
|
||||
|
||||
private byte[] createAck(boolean tooBig) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(new byte[HEADER_LENGTH]);
|
||||
BdfWriter w = bdfWriterFactory.createWriter(out);
|
||||
w.writeListStart();
|
||||
w.writeListStart();
|
||||
while (out.size() + UNIQUE_ID_LENGTH + LIST_END_LENGTH * 2
|
||||
< HEADER_LENGTH + MAX_PAYLOAD_LENGTH) {
|
||||
w.writeRaw(TestUtils.getRandomId());
|
||||
out.write(new byte[PACKET_HEADER_LENGTH]);
|
||||
while (out.size() + UniqueId.LENGTH <= PACKET_HEADER_LENGTH
|
||||
+ MAX_PACKET_PAYLOAD_LENGTH) {
|
||||
out.write(TestUtils.getRandomId());
|
||||
}
|
||||
if (tooBig) w.writeRaw(TestUtils.getRandomId());
|
||||
w.writeListEnd();
|
||||
w.writeListEnd();
|
||||
assertEquals(tooBig, out.size() > HEADER_LENGTH + MAX_PAYLOAD_LENGTH);
|
||||
if (tooBig) out.write(TestUtils.getRandomId());
|
||||
assertEquals(tooBig, out.size() > PACKET_HEADER_LENGTH +
|
||||
MAX_PACKET_PAYLOAD_LENGTH);
|
||||
byte[] packet = out.toByteArray();
|
||||
packet[1] = ACK;
|
||||
ByteUtils.writeUint16(packet.length - HEADER_LENGTH, packet, 2);
|
||||
ByteUtils.writeUint16(packet.length - PACKET_HEADER_LENGTH, packet, 2);
|
||||
return packet;
|
||||
}
|
||||
|
||||
private byte[] createEmptyAck() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(new byte[HEADER_LENGTH]);
|
||||
BdfWriter w = bdfWriterFactory.createWriter(out);
|
||||
w.writeListStart();
|
||||
w.writeListStart();
|
||||
w.writeListEnd();
|
||||
w.writeListEnd();
|
||||
byte[] packet = out.toByteArray();
|
||||
byte[] packet = new byte[PACKET_HEADER_LENGTH];
|
||||
packet[1] = ACK;
|
||||
ByteUtils.writeUint16(packet.length - HEADER_LENGTH, packet, 2);
|
||||
ByteUtils.writeUint16(packet.length - PACKET_HEADER_LENGTH, packet, 2);
|
||||
return packet;
|
||||
}
|
||||
|
||||
private byte[] createOffer(boolean tooBig) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(new byte[HEADER_LENGTH]);
|
||||
BdfWriter w = bdfWriterFactory.createWriter(out);
|
||||
w.writeListStart();
|
||||
w.writeListStart();
|
||||
while (out.size() + UNIQUE_ID_LENGTH + LIST_END_LENGTH * 2
|
||||
< HEADER_LENGTH + MAX_PAYLOAD_LENGTH) {
|
||||
w.writeRaw(TestUtils.getRandomId());
|
||||
out.write(new byte[PACKET_HEADER_LENGTH]);
|
||||
while (out.size() + UniqueId.LENGTH <= PACKET_HEADER_LENGTH
|
||||
+ MAX_PACKET_PAYLOAD_LENGTH) {
|
||||
out.write(TestUtils.getRandomId());
|
||||
}
|
||||
if (tooBig) w.writeRaw(TestUtils.getRandomId());
|
||||
w.writeListEnd();
|
||||
w.writeListEnd();
|
||||
assertEquals(tooBig, out.size() > HEADER_LENGTH + MAX_PAYLOAD_LENGTH);
|
||||
if (tooBig) out.write(TestUtils.getRandomId());
|
||||
assertEquals(tooBig, out.size() > PACKET_HEADER_LENGTH +
|
||||
MAX_PACKET_PAYLOAD_LENGTH);
|
||||
byte[] packet = out.toByteArray();
|
||||
packet[1] = OFFER;
|
||||
ByteUtils.writeUint16(packet.length - HEADER_LENGTH, packet, 2);
|
||||
ByteUtils.writeUint16(packet.length - PACKET_HEADER_LENGTH, packet, 2);
|
||||
return packet;
|
||||
}
|
||||
|
||||
private byte[] createEmptyOffer() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(new byte[HEADER_LENGTH]);
|
||||
BdfWriter w = bdfWriterFactory.createWriter(out);
|
||||
w.writeListStart();
|
||||
w.writeListStart();
|
||||
w.writeListEnd();
|
||||
w.writeListEnd();
|
||||
byte[] packet = out.toByteArray();
|
||||
byte[] packet = new byte[PACKET_HEADER_LENGTH];
|
||||
packet[1] = OFFER;
|
||||
ByteUtils.writeUint16(packet.length - HEADER_LENGTH, packet, 2);
|
||||
ByteUtils.writeUint16(packet.length - PACKET_HEADER_LENGTH, packet, 2);
|
||||
return packet;
|
||||
}
|
||||
|
||||
private byte[] createRequest(boolean tooBig) throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(new byte[HEADER_LENGTH]);
|
||||
BdfWriter w = bdfWriterFactory.createWriter(out);
|
||||
w.writeListStart();
|
||||
w.writeListStart();
|
||||
while (out.size() + UNIQUE_ID_LENGTH + LIST_END_LENGTH * 2
|
||||
< HEADER_LENGTH + MAX_PAYLOAD_LENGTH) {
|
||||
w.writeRaw(TestUtils.getRandomId());
|
||||
out.write(new byte[PACKET_HEADER_LENGTH]);
|
||||
while (out.size() + UniqueId.LENGTH <= PACKET_HEADER_LENGTH
|
||||
+ MAX_PACKET_PAYLOAD_LENGTH) {
|
||||
out.write(TestUtils.getRandomId());
|
||||
}
|
||||
if (tooBig) w.writeRaw(TestUtils.getRandomId());
|
||||
w.writeListEnd();
|
||||
w.writeListEnd();
|
||||
assertEquals(tooBig, out.size() > HEADER_LENGTH + MAX_PAYLOAD_LENGTH);
|
||||
if (tooBig) out.write(TestUtils.getRandomId());
|
||||
assertEquals(tooBig, out.size() > PACKET_HEADER_LENGTH +
|
||||
MAX_PACKET_PAYLOAD_LENGTH);
|
||||
byte[] packet = out.toByteArray();
|
||||
packet[1] = REQUEST;
|
||||
ByteUtils.writeUint16(packet.length - HEADER_LENGTH, packet, 2);
|
||||
ByteUtils.writeUint16(packet.length - PACKET_HEADER_LENGTH, packet, 2);
|
||||
return packet;
|
||||
}
|
||||
|
||||
private byte[] createEmptyRequest() throws Exception {
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
out.write(new byte[HEADER_LENGTH]);
|
||||
BdfWriter w = bdfWriterFactory.createWriter(out);
|
||||
w.writeListStart();
|
||||
w.writeListStart();
|
||||
w.writeListEnd();
|
||||
w.writeListEnd();
|
||||
byte[] packet = out.toByteArray();
|
||||
byte[] packet = new byte[PACKET_HEADER_LENGTH];
|
||||
packet[1] = REQUEST;
|
||||
ByteUtils.writeUint16(packet.length - HEADER_LENGTH, packet, 2);
|
||||
ByteUtils.writeUint16(packet.length - PACKET_HEADER_LENGTH, packet, 2);
|
||||
return packet;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,16 +22,14 @@ import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.messaging.MessagingManager;
|
||||
import org.briarproject.api.messaging.PrivateConversation;
|
||||
import org.briarproject.api.messaging.PrivateMessage;
|
||||
import org.briarproject.api.messaging.PrivateMessageFactory;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.Message;
|
||||
import org.briarproject.api.sync.MessageVerifier;
|
||||
import org.briarproject.api.sync.MessagingSession;
|
||||
import org.briarproject.api.sync.PacketReader;
|
||||
import org.briarproject.api.sync.PacketReaderFactory;
|
||||
import org.briarproject.api.sync.PacketWriter;
|
||||
import org.briarproject.api.sync.PacketWriterFactory;
|
||||
import org.briarproject.api.sync.SyncSession;
|
||||
import org.briarproject.api.transport.KeyManager;
|
||||
import org.briarproject.api.transport.StreamContext;
|
||||
import org.briarproject.api.transport.StreamReaderFactory;
|
||||
@@ -73,6 +71,8 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
private final TransportId transportId = new TransportId("id");
|
||||
private final SecretKey master = TestUtils.createSecretKey();
|
||||
private final long timestamp = System.currentTimeMillis();
|
||||
private final AuthorId aliceId = new AuthorId(TestUtils.getRandomId());
|
||||
private final AuthorId bobId = new AuthorId(TestUtils.getRandomId());
|
||||
|
||||
private Injector alice, bob;
|
||||
|
||||
@@ -106,14 +106,12 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
KeyManager keyManager = alice.getInstance(KeyManager.class);
|
||||
keyManager.start();
|
||||
// Add an identity for Alice
|
||||
AuthorId aliceId = new AuthorId(TestUtils.getRandomId());
|
||||
LocalAuthor aliceAuthor = new LocalAuthor(aliceId, "Alice",
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100], 1234);
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100], timestamp);
|
||||
IdentityManager identityManager =
|
||||
alice.getInstance(IdentityManager.class);
|
||||
identityManager.addLocalAuthor(aliceAuthor);
|
||||
// Add Bob as a contact
|
||||
AuthorId bobId = new AuthorId(TestUtils.getRandomId());
|
||||
Author bobAuthor = new Author(bobId, "Bob",
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||
ContactManager contactManager = alice.getInstance(ContactManager.class);
|
||||
@@ -121,19 +119,17 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
// Create the private conversation
|
||||
MessagingManager messagingManager =
|
||||
alice.getInstance(MessagingManager.class);
|
||||
messagingManager.addContact(contactId, master);
|
||||
messagingManager.addContact(contactId);
|
||||
// Derive and store the transport keys
|
||||
keyManager.addContact(contactId, Collections.singletonList(transportId),
|
||||
master, timestamp, true);
|
||||
// Send Bob a message
|
||||
byte[] body = "Hi Bob!".getBytes("UTF-8");
|
||||
PrivateMessageFactory messageFactory =
|
||||
PrivateMessageFactory privateMessageFactory =
|
||||
alice.getInstance(PrivateMessageFactory.class);
|
||||
GroupId groupId = messagingManager.getConversationId(contactId);
|
||||
PrivateConversation conversation =
|
||||
messagingManager.getConversation(groupId);
|
||||
Message message = messageFactory.createPrivateMessage(null,
|
||||
conversation, "text/plain", timestamp, body);
|
||||
byte[] body = "Hi Bob!".getBytes("UTF-8");
|
||||
PrivateMessage message = privateMessageFactory.createPrivateMessage(
|
||||
groupId, timestamp, null, "text/plain", body);
|
||||
messagingManager.addLocalMessage(message);
|
||||
// Get a stream context
|
||||
StreamContext ctx = keyManager.getStreamContext(contactId, transportId);
|
||||
@@ -150,7 +146,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
alice.getInstance(PacketWriterFactory.class);
|
||||
PacketWriter packetWriter = packetWriterFactory.createPacketWriter(
|
||||
streamWriter);
|
||||
MessagingSession session = new SimplexOutgoingSession(db,
|
||||
SyncSession session = new SimplexOutgoingSession(db,
|
||||
new ImmediateExecutor(), eventBus, contactId, transportId,
|
||||
MAX_LATENCY, packetWriter);
|
||||
// Write whatever needs to be written
|
||||
@@ -173,14 +169,12 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
KeyManager keyManager = bob.getInstance(KeyManager.class);
|
||||
keyManager.start();
|
||||
// Add an identity for Bob
|
||||
AuthorId bobId = new AuthorId(TestUtils.getRandomId());
|
||||
LocalAuthor bobAuthor = new LocalAuthor(bobId, "Bob",
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100], 1234);
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[100], timestamp);
|
||||
IdentityManager identityManager =
|
||||
bob.getInstance(IdentityManager.class);
|
||||
identityManager.addLocalAuthor(bobAuthor);
|
||||
// Add Alice as a contact
|
||||
AuthorId aliceId = new AuthorId(TestUtils.getRandomId());
|
||||
Author aliceAuthor = new Author(aliceId, "Alice",
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||
ContactManager contactManager = bob.getInstance(ContactManager.class);
|
||||
@@ -188,7 +182,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
// Create the private conversation
|
||||
MessagingManager messagingManager =
|
||||
bob.getInstance(MessagingManager.class);
|
||||
messagingManager.addContact(contactId, master);
|
||||
messagingManager.addContact(contactId);
|
||||
// Derive and store the transport keys
|
||||
keyManager.addContact(contactId, Collections.singletonList(transportId),
|
||||
master, timestamp, false);
|
||||
@@ -209,15 +203,13 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
streamReaderFactory.createStreamReader(in, ctx);
|
||||
// Create an incoming sync session
|
||||
EventBus eventBus = bob.getInstance(EventBus.class);
|
||||
MessageVerifier messageVerifier =
|
||||
bob.getInstance(MessageVerifier.class);
|
||||
PacketReaderFactory packetReaderFactory =
|
||||
bob.getInstance(PacketReaderFactory.class);
|
||||
PacketReader packetReader = packetReaderFactory.createPacketReader(
|
||||
streamReader);
|
||||
MessagingSession session = new IncomingSession(db,
|
||||
new ImmediateExecutor(), new ImmediateExecutor(), eventBus,
|
||||
messageVerifier, contactId, transportId, packetReader);
|
||||
SyncSession session = new IncomingSession(db,
|
||||
new ImmediateExecutor(), eventBus, contactId, transportId,
|
||||
packetReader);
|
||||
// No messages should have been added yet
|
||||
assertFalse(listener.messageAdded);
|
||||
// Read whatever needs to be read
|
||||
|
||||
@@ -12,11 +12,13 @@ public class ByteUtilsTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadUint16() {
|
||||
byte[] b = StringUtils.fromHexString("000000");
|
||||
byte[] b = StringUtils.fromHexString("00000000");
|
||||
assertEquals(0, ByteUtils.readUint16(b, 1));
|
||||
b = StringUtils.fromHexString("000001");
|
||||
b = StringUtils.fromHexString("00000100");
|
||||
assertEquals(1, ByteUtils.readUint16(b, 1));
|
||||
b = StringUtils.fromHexString("00FFFF");
|
||||
b = StringUtils.fromHexString("007FFF00");
|
||||
assertEquals(Short.MAX_VALUE, ByteUtils.readUint16(b, 1));
|
||||
b = StringUtils.fromHexString("00FFFF00");
|
||||
assertEquals(65535, ByteUtils.readUint16(b, 1));
|
||||
}
|
||||
|
||||
@@ -32,11 +34,13 @@ public class ByteUtilsTest extends BriarTestCase {
|
||||
|
||||
@Test
|
||||
public void testReadUint32() {
|
||||
byte[] b = StringUtils.fromHexString("0000000000");
|
||||
byte[] b = StringUtils.fromHexString("000000000000");
|
||||
assertEquals(0, ByteUtils.readUint32(b, 1));
|
||||
b = StringUtils.fromHexString("0000000001");
|
||||
b = StringUtils.fromHexString("000000000100");
|
||||
assertEquals(1, ByteUtils.readUint32(b, 1));
|
||||
b = StringUtils.fromHexString("00FFFFFFFF");
|
||||
b = StringUtils.fromHexString("007FFFFFFF00");
|
||||
assertEquals(Integer.MAX_VALUE, ByteUtils.readUint32(b, 1));
|
||||
b = StringUtils.fromHexString("00FFFFFFFF00");
|
||||
assertEquals(4294967295L, ByteUtils.readUint32(b, 1));
|
||||
}
|
||||
|
||||
@@ -50,6 +54,30 @@ public class ByteUtilsTest extends BriarTestCase {
|
||||
ByteUtils.readUint32(new byte[4], 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadUint64() {
|
||||
byte[] b = StringUtils.fromHexString("00000000000000000000");
|
||||
assertEquals(0L, ByteUtils.readUint64(b, 1));
|
||||
b = StringUtils.fromHexString("00000000000000000100");
|
||||
assertEquals(1L, ByteUtils.readUint64(b, 1));
|
||||
b = StringUtils.fromHexString("007FFFFFFFFFFFFFFF00");
|
||||
assertEquals(Long.MAX_VALUE, ByteUtils.readUint64(b, 1));
|
||||
b = StringUtils.fromHexString("00800000000000000000");
|
||||
assertEquals(Long.MIN_VALUE, ByteUtils.readUint64(b, 1));
|
||||
b = StringUtils.fromHexString("00FFFFFFFFFFFFFFFF00");
|
||||
assertEquals(-1L, ByteUtils.readUint64(b, 1));
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testReadUint64ValidatesArguments1() {
|
||||
ByteUtils.readUint64(new byte[7], 0);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testReadUint64ValidatesArguments2() {
|
||||
ByteUtils.readUint64(new byte[8], 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteUint16() {
|
||||
byte[] b = new byte[4];
|
||||
|
||||
Reference in New Issue
Block a user