package com.limegroup.gnutella.library;
import java.io.File;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import junit.framework.AssertionFailedError;
import org.limewire.gnutella.tests.LimeTestCase;
import org.limewire.gnutella.tests.LimeTestUtils;
import org.limewire.listener.EventListener;
import org.limewire.util.CommonUtils;
import org.limewire.util.TestUtils;
import com.google.inject.Inject;
import com.limegroup.gnutella.library.FileViewChangeEvent.Type;
public class FileViewManagerImplTest extends LimeTestCase {
private File f1, f2;
private FileDesc fd1, fd2;
@Inject private Library library;
@Inject private FileViewManagerImpl viewManager;
@Inject private FileCollectionManager collectionManager;
private SharedFileCollection c1;
private SharedFileCollection c2;
private String id1 = "id1";
private String id2 = "id2";
@Override
protected void setUp() throws Exception {
LimeTestUtils.createInjectorNonEagerly(LimeTestUtils.createModule(this));
f1 = TestUtils.getResourceInPackage("one.txt", getClass());
f2 = TestUtils.getResourceInPackage("two.txt", getClass());
FileManagerTestUtils.waitForLoad(library, 2000);
FileManagerTestUtils.assertAdds(library, f1, f2);
fd1 = library.getFileDesc(f1);
fd2 = library.getFileDesc(f2);
c1 = collectionManager.createNewCollection("1");
c2 = collectionManager.createNewCollection("2");
}
public void testCreateAfterExists() {
c1.add(f1);
c1.addFriend(id1);
FileView v = viewManager.getFileViewForId(id1);
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
Listener l = new Listener();
v.addListener(l);
l.assertNoChanges();
}
public void testFriendAdded() {
c1.add(f1);
FileView v = viewManager.getFileViewForId(id1);
Listener l = new Listener();
v.addListener(l);
c1.addFriend(id1);
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
FileViewChangeEvent e = l.getEventAndClear();
assertEquals(fd1, e.getFileDesc());
assertEquals(f1, e.getFile());
assertEquals(Type.FILE_ADDED, e.getType());
}
public void testFriendRemoved() {
c1.add(f1);
c1.addFriend(id1);
FileView v = viewManager.getFileViewForId(id1);
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
Listener l = new Listener();
v.addListener(l);
c1.removeFriend(id1);
assertEquals(0, v.size());
FileViewChangeEvent e = l.getEventAndClear();
assertEquals(fd1, e.getFileDesc());
assertEquals(f1, e.getFile());
assertEquals(Type.FILE_REMOVED, e.getType());
}
public void testFileAdded() {
c1.addFriend(id1);
FileView v = viewManager.getFileViewForId(id1);
Listener l = new Listener();
v.addListener(l);
c1.add(f1);
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
FileViewChangeEvent e = l.getEventAndClear();
assertEquals(fd1, e.getFileDesc());
assertEquals(f1, e.getFile());
assertEquals(Type.FILE_ADDED, e.getType());
}
public void testFileRemoved() {
FileView v = viewManager.getFileViewForId(id1);
assertEquals(0, v.size());
c1.add(f1);
c1.addFriend(id1);
assertEquals(1, v.size());
Listener l = new Listener();
v.addListener(l);
c1.remove(f1);
assertEquals(0, v.size());
FileViewChangeEvent e = l.getEventAndClear();
assertEquals(fd1, e.getFileDesc());
assertEquals(f1, e.getFile());
assertEquals(Type.FILE_REMOVED, e.getType());
}
public void testDuplicateFileAdded() {
FileView v = viewManager.getFileViewForId(id1);
assertEquals(0, v.size());
Listener l = new Listener();
v.addListener(l);
c1.add(f1);
c2.add(f1);
c1.addFriend(id1);
assertEquals(Type.FILE_ADDED, l.getEventAndClear().getType());
c2.addFriend(id1);
l.assertNoChanges();
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
}
public void testDuplicateFileRemoved() {
FileView v = viewManager.getFileViewForId(id1);
Listener l = new Listener();
v.addListener(l);
assertEquals(0, v.size());
c1.add(f1);
c2.add(f1);
c1.addFriend(id1);
c2.addFriend(id1);
assertEquals(1, v.size());
assertEquals(Type.FILE_ADDED, l.getEventAndClear().getType());
c2.remove(f1);
l.assertNoChanges();
assertEquals(1, v.size());
c1.remove(f1);
assertEquals(0, v.size());
FileViewChangeEvent e = l.getEventAndClear();
assertEquals(fd1, e.getFileDesc());
assertEquals(f1, e.getFile());
assertEquals(Type.FILE_REMOVED, e.getType());
}
public void testMultipleFriends() {
FileView v1 = viewManager.getFileViewForId(id1);
FileView v2 = viewManager.getFileViewForId(id2);
Listener l1 = new Listener();
Listener l2 = new Listener();
v1.addListener(l1);
v2.addListener(l2);
c1.add(f1);
c1.addFriend(id1);
assertEquals(1, v1.size());
assertEquals(0, v2.size());
assertEquals(fd1, v1.iterator().next());
assertEquals(Type.FILE_ADDED, l1.getEventAndClear().getType());
l2.assertNoChanges();
c2.add(f2);
c2.addFriend(id2);
assertEquals(1, v1.size());
assertEquals(1, v2.size());
assertEquals(fd1, v1.iterator().next());
assertEquals(fd2, v2.iterator().next());
l1.assertNoChanges();
assertEquals(Type.FILE_ADDED, l2.getEventAndClear().getType());
c1.addFriend(id2);
assertEquals(1, v1.size());
assertEquals(2, v2.size());
assertEquals(fd1, v1.iterator().next());
Iterator<FileDesc> v2Iter = v2.iterator();
assertEquals(fd1, v2Iter.next());
assertEquals(fd2, v2Iter.next());
l1.assertNoChanges();
assertEquals(Type.FILE_ADDED, l2.getEventAndClear().getType());
c2.removeFriend(id2);
assertEquals(1, v1.size());
assertEquals(1, v2.size());
assertEquals(fd1, v1.iterator().next());
assertEquals(fd1, v2.iterator().next());
l1.assertNoChanges();
assertEquals(Type.FILE_REMOVED, l2.getEventAndClear().getType());
}
public void testClearLibrary() {
c1.add(f1);
c1.addFriend(id1);
FileView v = viewManager.getFileViewForId(id1);
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
Listener l = new Listener();
v.addListener(l);
library.clear();
assertEquals(0, v.size());
assertEquals(Type.FILES_CLEARED, l.getEventAndClear().getType());
}
public void testClearCollection() {
c1.add(f1);
c1.addFriend(id1);
FileView v = viewManager.getFileViewForId(id1);
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
Listener l = new Listener();
v.addListener(l);
c1.clear();
assertEquals(0, v.size());
assertEquals(Type.FILE_REMOVED, l.getEventAndClear().getType());
}
public void testClearCollectionIfDuplicated() {
c1.add(f1);
c1.add(f2);
c1.addFriend(id1);
c2.add(f1);
c2.addFriend(id1);
FileView v = viewManager.getFileViewForId(id1);
assertEquals(2, v.size());
Iterator<FileDesc> vIter = v.iterator();
assertEquals(fd1, vIter.next());
assertEquals(fd2, vIter.next());
Listener l = new Listener();
v.addListener(l);
c1.clear();
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
FileViewChangeEvent e = l.getEventAndClear();
assertEquals(fd2, e.getFileDesc());
assertEquals(f2, e.getFile());
assertEquals(Type.FILE_REMOVED, e.getType());
}
public void testCollectionDeleted() {
c1.add(f1);
c1.addFriend(id1);
FileView v = viewManager.getFileViewForId(id1);
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
Listener l = new Listener();
v.addListener(l);
collectionManager.removeCollectionById(c1.getId());
assertEquals(0, v.size());
assertEquals(Type.FILE_REMOVED, l.getEventAndClear().getType());
}
public void testCollectionDeletedIfDuplicated() {
c1.add(f1);
c1.add(f2);
c1.addFriend(id1);
c2.add(f1);
c2.addFriend(id1);
FileView v = viewManager.getFileViewForId(id1);
assertEquals(2, v.size());
Iterator<FileDesc> vIter = v.iterator();
assertEquals(fd1, vIter.next());
assertEquals(fd2, vIter.next());
Listener l = new Listener();
v.addListener(l);
collectionManager.removeCollectionById(c1.getId());
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
FileViewChangeEvent e = l.getEventAndClear();
assertEquals(fd2, e.getFileDesc());
assertEquals(f2, e.getFile());
assertEquals(Type.FILE_REMOVED, e.getType());
}
public void testFriendsSet() {
c1.add(f1);
FileView v1 = viewManager.getFileViewForId(id1);
FileView v2 = viewManager.getFileViewForId(id2);
assertEquals(0, v1.size());
assertEquals(0, v2.size());
Listener l1 = new Listener();
v1.addListener(l1);
Listener l2 = new Listener();
v2.addListener(l2);
c1.setFriendList(Arrays.asList(id1));
assertEquals(1, v1.size());
assertEquals(0, v2.size());
assertEquals(fd1, v1.iterator().next());
assertEquals(Type.FILE_ADDED, l1.getEventAndClear().getType());
l2.assertNoChanges();
c1.setFriendList(Arrays.asList(id1, id2));
assertEquals(1, v1.size());
assertEquals(1, v2.size());
assertEquals(fd1, v1.iterator().next());
assertEquals(fd1, v2.iterator().next());
l1.assertNoChanges();
assertEquals(Type.FILE_ADDED, l2.getEventAndClear().getType());
c1.setFriendList(Arrays.asList(id2));
assertEquals(0, v1.size());
assertEquals(1, v2.size());
assertEquals(fd1, v2.iterator().next());
assertEquals(Type.FILE_REMOVED, l1.getEventAndClear().getType());
l2.assertNoChanges();
c1.setFriendList(Collections.<String>emptyList());
assertEquals(0, v1.size());
assertEquals(0, v2.size());
l1.assertNoChanges();
assertEquals(Type.FILE_REMOVED, l2.getEventAndClear().getType());
}
public void testFileChanged() throws Exception {
FileView v = viewManager.getFileViewForId(id1);
c1.add(f1);
c1.addFriend(id1);
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
Listener l = new Listener();
v.addListener(l);
l.assertNoChanges();
FileManagerTestUtils.assertFileChanges(library, f1);
FileDesc newFd1 = library.getFileDesc(f1);
assertEquals(1, v.size());
assertNotSame(fd1, v.iterator().next());
assertEquals(newFd1, v.iterator().next());
// Note: Because when a FileDesc is changed it has to recalculate the URNs,
// that means the the library sends two events: FILE_CHANGED & FILE_META_CHANGED,
// the meta changing after the new URN is calculated. The initial FILE_CHANGED
// is for a FileDesc that has no URN or metadata!!
// This has the effect of causing a view to temporarily remove the file,
// waiting for the URN to calculate.
// The end result is that the view sends two events: REMOVE, ADD.
// It removes when the change happens (because it can't share a file w/o a URN),
// and it re-adds when the meta changes, because it can share it now.
List<FileViewChangeEvent> events = l.getEventsAndClear(2);
FileViewChangeEvent remove = events.get(0);
assertSame(fd1, remove.getFileDesc());
assertEquals(f1, remove.getFile());
assertEquals(Type.FILE_REMOVED, remove.getType());
FileViewChangeEvent add = events.get(1);
assertSame(newFd1, add.getFileDesc());
assertEquals(f1, add.getFile());
assertEquals(Type.FILE_ADDED, add.getType());
}
public void testFileChangedWithDuplicates() throws Exception {
FileView v = viewManager.getFileViewForId(id1);
c1.add(f1);
c1.addFriend(id1);
c2.add(f1);
c2.addFriend(id1);
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
Listener l = new Listener();
v.addListener(l);
FileManagerTestUtils.assertFileChanges(library, f1);
FileDesc newFd1 = library.getFileDesc(f1);
assertEquals(1, v.size());
assertNotSame(fd1, v.iterator().next());
assertEquals(newFd1, v.iterator().next());
// Note: Because when a FileDesc is changed it has to recalculate the URNs,
// that means the the library sends two events: FILE_CHANGED & FILE_META_CHANGED,
// the meta changing after the new URN is calculated. The initial FILE_CHANGED
// is for a FileDesc that has no URN or metadata!!
// This has the effect of causing a view to temporarily remove the file,
// waiting for the URN to calculate.
// The end result is that the view sends two events: REMOVE, ADD.
// It removes when the change happens (because it can't share a file w/o a URN),
// and it re-adds when the meta changes, because it can share it now.
// Further complication:
// Because each collection resends a META_CHANGE when it hears it,
// we learn about multiple META_CHANGES. Each view also needs to resend
// the META_CHANGE, so listeners can be kept up to date with changes to the
// metadata. It's hard to distinguish between a valid META_CHANGE and one
// that existed because two different collections shared with a single person.
// So, for now, we just accept that an additional META_CHANGE will be sent.
List<FileViewChangeEvent> events = l.getEventsAndClear(3);
FileViewChangeEvent remove = events.get(0);
assertSame(fd1, remove.getFileDesc());
assertEquals(f1, remove.getFile());
assertEquals(Type.FILE_REMOVED, remove.getType());
FileViewChangeEvent add = events.get(1);
assertSame(newFd1, add.getFileDesc());
assertEquals(f1, add.getFile());
assertEquals(Type.FILE_ADDED, add.getType());
FileViewChangeEvent meta = events.get(2);
assertSame(newFd1, meta.getFileDesc());
assertEquals(f1, meta.getFile());
assertEquals(Type.FILE_META_CHANGED, meta.getType());
}
public void testFileChangeFails() throws Exception {
FileView v = viewManager.getFileViewForId(id1);
File copy1 = new File("f1 copy.txt").getCanonicalFile();
CommonUtils.copyFile(f1, copy1);
FileDesc copyFd1;
try {
FileManagerTestUtils.assertAdds(c1, copy1);
copyFd1 = library.getFileDesc(copy1);
c1.addFriend(id1);
assertEquals(1, v.size());
assertEquals(copyFd1, v.iterator().next());
} finally {
copy1.delete();
}
Listener l = new Listener();
v.addListener(l);
FileManagerTestUtils.assertFileChangedFails(FileViewChangeFailedException.Reason.NOT_MANAGEABLE, library, copy1);
assertEquals(0, v.size());
FileViewChangeEvent e = l.getEventAndClear();
assertEquals(copyFd1, e.getFileDesc());
assertEquals(copy1, e.getFile());
assertEquals(Type.FILE_REMOVED, e.getType());
}
public void testFileRenamed() throws Exception {
FileView v = viewManager.getFileViewForId(id1);
c1.add(f1);
c1.addFriend(id1);
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
Listener l = new Listener();
v.addListener(l);
File copy1 = new File("f1 copy.txt").getCanonicalFile();
CommonUtils.copyFile(f1, copy1);
try {
FileManagerTestUtils.assertFileRenames(library, f1, copy1);
assertEquals(1, v.size());
assertNotSame(fd1, v.iterator().next());
assertEquals(library.getFileDesc(copy1), v.iterator().next());
} finally {
copy1.delete();
}
FileViewChangeEvent e = l.getEventAndClear();
assertEquals(f1, e.getOldFile());
assertEquals(fd1, e.getOldValue());
assertEquals(copy1, e.getFile());
assertEquals(library.getFileDesc(copy1), e.getFileDesc());
assertEquals(Type.FILE_CHANGED, e.getType());
}
public void testFileRenamesWithDuplicates() throws Exception {
FileView v = viewManager.getFileViewForId(id1);
c1.add(f1);
c1.addFriend(id1);
c2.add(f1);
c2.addFriend(id1);
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
Listener l = new Listener();
v.addListener(l);
File copy1 = new File("f1 copy.txt").getCanonicalFile();
CommonUtils.copyFile(f1, copy1);
try {
FileManagerTestUtils.assertFileRenames(library, f1, copy1);
assertEquals(1, v.size());
assertNotSame(fd1, v.iterator().next());
assertEquals(library.getFileDesc(copy1), v.iterator().next());
} finally {
copy1.delete();
}
FileViewChangeEvent e = l.getEventAndClear();
assertEquals(f1, e.getOldFile());
assertEquals(fd1, e.getOldValue());
assertEquals(copy1, e.getFile());
assertEquals(library.getFileDesc(copy1), e.getFileDesc());
assertEquals(Type.FILE_CHANGED, e.getType());
}
public void testRenameFails() throws Exception {
FileView v = viewManager.getFileViewForId(id1);
c1.add(f1);
c1.addFriend(id1);
assertEquals(1, v.size());
assertEquals(fd1, v.iterator().next());
Listener l = new Listener();
v.addListener(l);
File fake = new File("fake").getCanonicalFile();
FileManagerTestUtils.assertFileRenameFails(FileViewChangeFailedException.Reason.NOT_MANAGEABLE, library, f1, fake);
assertEquals(0, v.size());
FileViewChangeEvent e = l.getEventAndClear();
assertEquals(fd1, e.getFileDesc());
assertEquals(f1, e.getFile());
assertEquals(Type.FILE_REMOVED, e.getType());
}
private static class Listener implements EventListener<FileViewChangeEvent> {
private final List<FileViewChangeEvent> changes = new ArrayList<FileViewChangeEvent>();
@Override
public void handleEvent(FileViewChangeEvent event) {
changes.add(event);
}
FileViewChangeEvent getEventAndClear() {
if(changes.isEmpty()) {
throw new AssertionFailedError("no changes!");
} else if(changes.size() > 1) {
throw new AssertionFailedError("More than 1 change: " + changes);
} else {
return changes.remove(0);
}
}
List<FileViewChangeEvent> getEventsAndClear(int expected) {
if(changes.isEmpty()) {
throw new AssertionFailedError("no changes!");
} else if(changes.size() != expected) {
throw new AssertionFailedError("Unexpected changecount: " + changes.size() + ", changes: " + changes);
} else {
List<FileViewChangeEvent> list = new ArrayList<FileViewChangeEvent>(changes);
changes.clear();
return list;
}
}
void assertNoChanges() {
assertTrue(changes.toString(), changes.isEmpty());
}
}
}