mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Add integration tests for GroupInvitationManager
This commit is contained in:
@@ -39,6 +39,7 @@ import org.briarproject.introduction.IntroductionGroupFactory;
|
||||
import org.briarproject.introduction.IntroductionModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.privategroup.PrivateGroupModule;
|
||||
import org.briarproject.privategroup.invitation.GroupInvitationModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
@@ -180,6 +181,7 @@ public abstract class BriarIntegrationTest extends BriarTestCase {
|
||||
component.inject(new CryptoModule.EagerSingletons());
|
||||
component.inject(new ContactModule.EagerSingletons());
|
||||
component.inject(new ForumModule.EagerSingletons());
|
||||
component.inject(new GroupInvitationModule.EagerSingletons());
|
||||
component.inject(new IntroductionModule.EagerSingletons());
|
||||
component.inject(new PropertiesModule.EagerSingletons());
|
||||
component.inject(new PrivateGroupModule.EagerSingletons());
|
||||
|
||||
@@ -14,6 +14,7 @@ import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.introduction.IntroductionManager;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.privategroup.PrivateGroupManager;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationManager;
|
||||
import org.briarproject.api.properties.TransportPropertyManager;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.blogs.BlogsModule;
|
||||
@@ -78,6 +79,8 @@ public interface BriarIntegrationTestComponent {
|
||||
|
||||
void inject(ForumModule.EagerSingletons init);
|
||||
|
||||
void inject(GroupInvitationModule.EagerSingletons init);
|
||||
|
||||
void inject(IntroductionModule.EagerSingletons init);
|
||||
|
||||
void inject(LifecycleModule.EagerSingletons init);
|
||||
@@ -114,6 +117,8 @@ public interface BriarIntegrationTestComponent {
|
||||
|
||||
ForumManager getForumManager();
|
||||
|
||||
GroupInvitationManager getGroupInvitationManager();
|
||||
|
||||
IntroductionManager getIntroductionManager();
|
||||
|
||||
MessageTracker getMessageTracker();
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
package org.briarproject;
|
||||
|
||||
import org.briarproject.api.clients.ProtocolStateException;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.privategroup.GroupMessage;
|
||||
import org.briarproject.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.api.privategroup.PrivateGroupManager;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationItem;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationManager;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationRequest;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationResponse;
|
||||
import org.briarproject.api.sharing.InvitationMessage;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import static junit.framework.TestCase.fail;
|
||||
import static org.briarproject.TestUtils.assertGroupCount;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class GroupInvitationIntegrationTest extends BriarIntegrationTest {
|
||||
|
||||
private PrivateGroup privateGroup0;
|
||||
private PrivateGroupManager groupManager0, groupManager1;
|
||||
private GroupInvitationManager groupInvitationManager0,
|
||||
groupInvitationManager1;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
groupManager0 = c0.getPrivateGroupManager();
|
||||
groupManager1 = c1.getPrivateGroupManager();
|
||||
groupInvitationManager0 = c0.getGroupInvitationManager();
|
||||
groupInvitationManager1 = c1.getGroupInvitationManager();
|
||||
|
||||
privateGroup0 =
|
||||
privateGroupFactory.createPrivateGroup("Testgroup", author0);
|
||||
long joinTime = clock.currentTimeMillis();
|
||||
GroupMessage joinMsg0 = groupMessageFactory
|
||||
.createJoinMessage(privateGroup0.getId(), joinTime, author0);
|
||||
groupManager0.addPrivateGroup(privateGroup0, joinMsg0, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendInvitation() throws Exception {
|
||||
long timestamp = clock.currentTimeMillis();
|
||||
String msg = "Hi!";
|
||||
sendInvitation(timestamp, msg);
|
||||
|
||||
sync0To1(1, true);
|
||||
|
||||
Collection<GroupInvitationItem> invitations =
|
||||
groupInvitationManager1.getInvitations();
|
||||
assertEquals(1, invitations.size());
|
||||
GroupInvitationItem item = invitations.iterator().next();
|
||||
assertEquals(contact0From1, item.getCreator());
|
||||
assertEquals(privateGroup0, item.getShareable());
|
||||
assertEquals(privateGroup0.getId(), item.getId());
|
||||
assertEquals(privateGroup0.getName(), item.getName());
|
||||
assertFalse(item.isSubscribed());
|
||||
|
||||
Collection<InvitationMessage> messages =
|
||||
groupInvitationManager1.getInvitationMessages(contactId0From1);
|
||||
assertEquals(1, messages.size());
|
||||
GroupInvitationRequest request =
|
||||
(GroupInvitationRequest) messages.iterator().next();
|
||||
assertEquals(msg, request.getMessage());
|
||||
assertEquals(author0, request.getCreator());
|
||||
assertEquals(timestamp, request.getTimestamp());
|
||||
assertEquals(contactId0From1, request.getContactId());
|
||||
assertEquals(privateGroup0.getName(), request.getGroupName());
|
||||
assertFalse(request.isLocal());
|
||||
assertFalse(request.isRead());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvitationDecline() throws Exception {
|
||||
long timestamp = clock.currentTimeMillis();
|
||||
sendInvitation(timestamp, null);
|
||||
|
||||
sync0To1(1, true);
|
||||
assertFalse(groupInvitationManager1.getInvitations().isEmpty());
|
||||
|
||||
groupInvitationManager1
|
||||
.respondToInvitation(contactId0From1, privateGroup0, false);
|
||||
|
||||
Collection<InvitationMessage> messages =
|
||||
groupInvitationManager1.getInvitationMessages(contactId0From1);
|
||||
assertEquals(2, messages.size());
|
||||
for (InvitationMessage m : messages) {
|
||||
if (m instanceof GroupInvitationResponse) {
|
||||
GroupInvitationResponse response = (GroupInvitationResponse) m;
|
||||
assertEquals(contactId0From1, response.getContactId());
|
||||
assertTrue(response.isLocal());
|
||||
assertFalse(response.wasAccepted());
|
||||
}
|
||||
}
|
||||
|
||||
sync1To0(1, true);
|
||||
|
||||
messages =
|
||||
groupInvitationManager0.getInvitationMessages(contactId1From0);
|
||||
assertEquals(2, messages.size());
|
||||
for (InvitationMessage m : messages) {
|
||||
if (m instanceof GroupInvitationResponse) {
|
||||
GroupInvitationResponse response = (GroupInvitationResponse) m;
|
||||
assertEquals(contactId0From1, response.getContactId());
|
||||
assertFalse(response.isLocal());
|
||||
assertFalse(response.wasAccepted());
|
||||
}
|
||||
}
|
||||
// no invitations are open
|
||||
assertTrue(groupInvitationManager1.getInvitations().isEmpty());
|
||||
// no groups were added
|
||||
assertEquals(0, groupManager1.getPrivateGroups().size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvitationAccept() throws Exception {
|
||||
long timestamp = clock.currentTimeMillis();
|
||||
sendInvitation(timestamp, null);
|
||||
|
||||
sync0To1(1, true);
|
||||
assertFalse(groupInvitationManager1.getInvitations().isEmpty());
|
||||
|
||||
groupInvitationManager1
|
||||
.respondToInvitation(contactId0From1, privateGroup0, true);
|
||||
|
||||
Collection<InvitationMessage> messages =
|
||||
groupInvitationManager1.getInvitationMessages(contactId0From1);
|
||||
assertEquals(2, messages.size());
|
||||
for (InvitationMessage m : messages) {
|
||||
if (m instanceof GroupInvitationResponse) {
|
||||
GroupInvitationResponse response = (GroupInvitationResponse) m;
|
||||
assertTrue(response.wasAccepted());
|
||||
}
|
||||
}
|
||||
|
||||
sync1To0(1, true);
|
||||
|
||||
messages =
|
||||
groupInvitationManager0.getInvitationMessages(contactId1From0);
|
||||
assertEquals(2, messages.size());
|
||||
for (InvitationMessage m : messages) {
|
||||
if (m instanceof GroupInvitationResponse) {
|
||||
GroupInvitationResponse response = (GroupInvitationResponse) m;
|
||||
assertTrue(response.wasAccepted());
|
||||
}
|
||||
}
|
||||
// no invitations are open
|
||||
assertTrue(groupInvitationManager1.getInvitations().isEmpty());
|
||||
// group was added
|
||||
Collection<PrivateGroup> groups = groupManager1.getPrivateGroups();
|
||||
assertEquals(1, groups.size());
|
||||
assertEquals(privateGroup0, groups.iterator().next());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroupCount() throws Exception {
|
||||
long timestamp = clock.currentTimeMillis();
|
||||
sendInvitation(timestamp, null);
|
||||
|
||||
// 0 has one read outgoing message
|
||||
Group g1 = groupInvitationManager0.getContactGroup(contact1From0);
|
||||
assertGroupCount(messageTracker0, g1.getId(), 1, 0, timestamp);
|
||||
|
||||
sync0To1(1, true);
|
||||
|
||||
// 1 has one unread message
|
||||
Group g0 = groupInvitationManager1.getContactGroup(contact0From1);
|
||||
assertGroupCount(messageTracker1, g0.getId(), 1, 1, timestamp);
|
||||
InvitationMessage m =
|
||||
groupInvitationManager1.getInvitationMessages(contactId0From1)
|
||||
.iterator().next();
|
||||
|
||||
groupInvitationManager1
|
||||
.respondToInvitation(contactId0From1, privateGroup0, true);
|
||||
|
||||
// 1 has two messages, one still unread
|
||||
assertGroupCount(messageTracker1, g0.getId(), 2, 1);
|
||||
|
||||
// now all messages should be read
|
||||
groupInvitationManager1.setReadFlag(g0.getId(), m.getId(), true);
|
||||
assertGroupCount(messageTracker1, g0.getId(), 2, 0);
|
||||
|
||||
sync1To0(1, true);
|
||||
|
||||
// now 0 has two messages, one of them unread
|
||||
assertGroupCount(messageTracker0, g1.getId(), 2, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleInvitations() throws Exception {
|
||||
sendInvitation(clock.currentTimeMillis(), null);
|
||||
|
||||
// invitation is not allowed before the first hasn't been answered
|
||||
assertFalse(groupInvitationManager0
|
||||
.isInvitationAllowed(contact1From0, privateGroup0.getId()));
|
||||
|
||||
// deliver invitation and response
|
||||
sync0To1(1, true);
|
||||
groupInvitationManager1
|
||||
.respondToInvitation(contactId0From1, privateGroup0, false);
|
||||
sync1To0(1, true);
|
||||
|
||||
// after invitation was declined, inviting again is possible
|
||||
assertTrue(groupInvitationManager0
|
||||
.isInvitationAllowed(contact1From0, privateGroup0.getId()));
|
||||
|
||||
// send and accept the second invitation
|
||||
sendInvitation(clock.currentTimeMillis(), "Second Invitation");
|
||||
sync0To1(1, true);
|
||||
groupInvitationManager1
|
||||
.respondToInvitation(contactId0From1, privateGroup0, true);
|
||||
sync1To0(1, true);
|
||||
|
||||
// invitation is not allowed since the member joined the group now
|
||||
assertFalse(groupInvitationManager0
|
||||
.isInvitationAllowed(contact1From0, privateGroup0.getId()));
|
||||
|
||||
// don't allow another invitation request
|
||||
try {
|
||||
sendInvitation(clock.currentTimeMillis(), "Third Invitation");
|
||||
fail();
|
||||
} catch (ProtocolStateException e) {
|
||||
// expected
|
||||
}
|
||||
}
|
||||
|
||||
@Test(expected = ProtocolStateException.class)
|
||||
public void testInvitationsWithSameTimestamp() throws Exception {
|
||||
long timestamp = clock.currentTimeMillis();
|
||||
sendInvitation(timestamp, null);
|
||||
sync0To1(1, true);
|
||||
|
||||
groupInvitationManager1
|
||||
.respondToInvitation(contactId0From1, privateGroup0, false);
|
||||
sync1To0(1, true);
|
||||
|
||||
sendInvitation(timestamp, "Second Invitation");
|
||||
sync0To1(1, true);
|
||||
|
||||
groupInvitationManager1
|
||||
.respondToInvitation(contactId0From1, privateGroup0, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreatorLeavesBeforeInvitationAnswered() throws Exception {
|
||||
sendInvitation(clock.currentTimeMillis(), null);
|
||||
sync0To1(1, true);
|
||||
|
||||
groupManager0.removePrivateGroup(privateGroup0.getId());
|
||||
assertEquals(0, groupManager0.getPrivateGroups().size());
|
||||
// sync0To1(1, true);
|
||||
// FIXME
|
||||
groupInvitationManager1
|
||||
.respondToInvitation(contactId0From1, privateGroup0, true);
|
||||
sync1To0(1, true);
|
||||
|
||||
assertEquals(0, groupManager1.getPrivateGroups().size());
|
||||
}
|
||||
|
||||
private void sendInvitation(long timestamp, @Nullable String msg) throws
|
||||
DbException {
|
||||
byte[] signature = groupInvitationFactory.signInvitation(contact1From0,
|
||||
privateGroup0.getId(), timestamp, author0.getPrivateKey());
|
||||
groupInvitationManager0
|
||||
.sendInvitation(privateGroup0.getId(), contactId1From0, msg,
|
||||
timestamp, signature);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package org.briarproject;
|
||||
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.identity.AuthorId;
|
||||
import org.briarproject.api.privategroup.GroupMember;
|
||||
import org.briarproject.api.privategroup.GroupMessage;
|
||||
import org.briarproject.api.privategroup.GroupMessageHeader;
|
||||
import org.briarproject.api.privategroup.JoinMessageHeader;
|
||||
import org.briarproject.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.api.privategroup.PrivateGroupManager;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationManager;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import static org.briarproject.api.identity.Author.Status.OURSELVES;
|
||||
import static org.briarproject.api.privategroup.Visibility.INVISIBLE;
|
||||
import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_CONTACT;
|
||||
import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_US;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
/**
|
||||
* This class tests how PrivateGroupManager and GroupInvitationManager
|
||||
* play together.
|
||||
*/
|
||||
public class PrivateGroupIntegrationTest extends BriarIntegrationTest {
|
||||
|
||||
private GroupId groupId0;
|
||||
private PrivateGroup privateGroup0;
|
||||
private PrivateGroupManager groupManager0, groupManager1, groupManager2;
|
||||
private GroupInvitationManager groupInvitationManager0,
|
||||
groupInvitationManager1, groupInvitationManager2;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
super.setUp();
|
||||
|
||||
groupManager0 = c0.getPrivateGroupManager();
|
||||
groupManager1 = c1.getPrivateGroupManager();
|
||||
groupManager2 = c2.getPrivateGroupManager();
|
||||
groupInvitationManager0 = c0.getGroupInvitationManager();
|
||||
groupInvitationManager1 = c1.getGroupInvitationManager();
|
||||
groupInvitationManager2 = c2.getGroupInvitationManager();
|
||||
|
||||
privateGroup0 =
|
||||
privateGroupFactory.createPrivateGroup("Test Group", author0);
|
||||
groupId0 = privateGroup0.getId();
|
||||
long joinTime = clock.currentTimeMillis();
|
||||
GroupMessage joinMsg0 = groupMessageFactory
|
||||
.createJoinMessage(groupId0, joinTime, author0);
|
||||
groupManager0.addPrivateGroup(privateGroup0, joinMsg0, true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMembership() throws Exception {
|
||||
sendInvitation(contactId1From0, clock.currentTimeMillis(), "Hi!");
|
||||
|
||||
// our group has only one member (ourselves)
|
||||
Collection<GroupMember> members = groupManager0.getMembers(groupId0);
|
||||
assertEquals(1, members.size());
|
||||
assertEquals(author0, members.iterator().next().getAuthor());
|
||||
assertEquals(OURSELVES, members.iterator().next().getStatus());
|
||||
|
||||
sync0To1(1, true);
|
||||
groupInvitationManager1
|
||||
.respondToInvitation(contactId0From1, privateGroup0, true);
|
||||
sync1To0(1, true);
|
||||
|
||||
// sync group join messages
|
||||
sync0To1(2, true); // + one invitation protocol join message
|
||||
sync1To0(1, true);
|
||||
|
||||
// now the group has two members
|
||||
members = groupManager0.getMembers(groupId0);
|
||||
assertEquals(2, members.size());
|
||||
members = groupManager1.getMembers(groupId0);
|
||||
assertEquals(2, members.size());
|
||||
|
||||
// make sure 1's member list is as expected
|
||||
for (GroupMember m : members) {
|
||||
if (m.getStatus() != OURSELVES) {
|
||||
assertEquals(author0.getId(), m.getAuthor().getId());
|
||||
} else {
|
||||
assertEquals(author1.getId(), m.getAuthor().getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRevealContacts() throws Exception {
|
||||
// invite two contacts
|
||||
sendInvitation(contactId1From0, clock.currentTimeMillis(), "Hi 1!");
|
||||
sendInvitation(contactId2From0, clock.currentTimeMillis(), "Hi 2!");
|
||||
sync0To1(1, true);
|
||||
sync0To2(1, true);
|
||||
|
||||
// accept both invitations
|
||||
groupInvitationManager1
|
||||
.respondToInvitation(contactId0From1, privateGroup0, true);
|
||||
groupInvitationManager2
|
||||
.respondToInvitation(contactId0From2, privateGroup0, true);
|
||||
sync1To0(1, true);
|
||||
sync2To0(1, true);
|
||||
|
||||
// sync group join messages
|
||||
sync0To1(2, true); // + one invitation protocol join message
|
||||
assertEquals(2, groupManager1.getMembers(groupId0).size());
|
||||
sync1To0(1, true);
|
||||
assertEquals(2, groupManager0.getMembers(groupId0).size());
|
||||
sync0To2(3, true); // 2 join messages and 1 invite join message
|
||||
assertEquals(3, groupManager2.getMembers(groupId0).size());
|
||||
sync2To0(1, true);
|
||||
assertEquals(3, groupManager0.getMembers(groupId0).size());
|
||||
sync0To1(1, true);
|
||||
assertEquals(3, groupManager1.getMembers(groupId0).size());
|
||||
|
||||
// 1 and 2 add each other as contacts
|
||||
addContacts1And2();
|
||||
|
||||
// their relationship is still invisible
|
||||
assertEquals(INVISIBLE,
|
||||
getGroupMember(groupManager1, author2.getId()).getVisibility());
|
||||
assertEquals(INVISIBLE,
|
||||
getGroupMember(groupManager2, author1.getId()).getVisibility());
|
||||
|
||||
// 1 reveals the contact relationship to 2
|
||||
assertTrue(contactId2From1 != null);
|
||||
groupInvitationManager1.revealRelationship(contactId2From1, groupId0);
|
||||
sync1To2(1, true);
|
||||
sync2To1(1, true);
|
||||
|
||||
// their relationship is now revealed
|
||||
assertEquals(REVEALED_BY_US,
|
||||
getGroupMember(groupManager1, author2.getId()).getVisibility());
|
||||
assertEquals(REVEALED_BY_CONTACT,
|
||||
getGroupMember(groupManager2, author1.getId()).getVisibility());
|
||||
|
||||
// 2 sends a message to the group
|
||||
long time = clock.currentTimeMillis();
|
||||
String body = "This is a test message!";
|
||||
MessageId previousMsgId = groupManager2.getPreviousMsgId(groupId0);
|
||||
GroupMessage msg = groupMessageFactory
|
||||
.createGroupMessage(groupId0, time, null, author2, body,
|
||||
previousMsgId);
|
||||
groupManager2.addLocalMessage(msg);
|
||||
|
||||
// 1 has only the three join messages in the group
|
||||
Collection<GroupMessageHeader> headers =
|
||||
groupManager1.getHeaders(groupId0);
|
||||
assertEquals(3, headers.size());
|
||||
|
||||
// message should sync to 1 without creator (0) being involved
|
||||
sync2To1(1, true);
|
||||
headers = groupManager1.getHeaders(groupId0);
|
||||
assertEquals(4, headers.size());
|
||||
for (GroupMessageHeader h : headers) {
|
||||
if (h instanceof JoinMessageHeader) continue;
|
||||
assertEquals(time, h.getTimestamp());
|
||||
assertEquals(groupId0, h.getGroupId());
|
||||
assertEquals(author2.getId(), h.getAuthor().getId());
|
||||
}
|
||||
|
||||
// message should sync from 1 to 0 without 2 being involved
|
||||
sync1To0(1, true);
|
||||
headers = groupManager0.getHeaders(groupId0);
|
||||
assertEquals(4, headers.size());
|
||||
}
|
||||
|
||||
private void sendInvitation(ContactId c, long timestamp,
|
||||
@Nullable String msg) throws DbException {
|
||||
Contact contact = contactManager0.getContact(c);
|
||||
byte[] signature = groupInvitationFactory
|
||||
.signInvitation(contact, groupId0, timestamp,
|
||||
author0.getPrivateKey());
|
||||
groupInvitationManager0
|
||||
.sendInvitation(groupId0, c, msg, timestamp, signature);
|
||||
}
|
||||
|
||||
private GroupMember getGroupMember(PrivateGroupManager groupManager,
|
||||
AuthorId a) throws DbException {
|
||||
Collection<GroupMember> members = groupManager.getMembers(groupId0);
|
||||
for (GroupMember m : members) {
|
||||
if (m.getAuthor().getId().equals(a)) return m;
|
||||
}
|
||||
fail();
|
||||
throw new RuntimeException();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user