From 6d6f0b486ac42b6bd69ddb8862ee3af7b819f3a7 Mon Sep 17 00:00:00 2001 From: Keuin Date: Mon, 15 Feb 2021 17:52:53 +0800 Subject: Add tests. Implementing auto backup and configuration (not finished) --- .../com/keuin/kbackupfabric/ConfigInitializer.java | 8 + .../com/keuin/kbackupfabric/KBConfiguration.java | 33 ++++ .../autobackup/AutoBackupScheduler.java | 50 +++++ .../autobackup/PlayerActivityTracker.java | 15 ++ .../incremental/ObjectCollectionIterator.java | 3 - .../identifier/SingleHashIdentifier.java | 10 +- .../manager/IncrementalBackupStorageManager.java | 51 +++++- .../handler/ConcretePlayerConnectEventHandler.java | 22 +++ .../incremental/ObjectCollectionIteratorTest.java | 49 +++++ .../identifier/SingleHashIdentifierTest.java | 35 ++++ .../IncrementalBackupStorageManagerTest.java | 201 +++++++++++++++++++++ 11 files changed, 459 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/keuin/kbackupfabric/ConfigInitializer.java create mode 100644 src/main/java/com/keuin/kbackupfabric/KBConfiguration.java create mode 100644 src/main/java/com/keuin/kbackupfabric/autobackup/AutoBackupScheduler.java create mode 100644 src/main/java/com/keuin/kbackupfabric/autobackup/PlayerActivityTracker.java create mode 100644 src/main/java/com/keuin/kbackupfabric/event/handler/ConcretePlayerConnectEventHandler.java create mode 100644 src/test/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollectionIteratorTest.java create mode 100644 src/test/java/com/keuin/kbackupfabric/backup/incremental/identifier/SingleHashIdentifierTest.java create mode 100644 src/test/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManagerTest.java diff --git a/src/main/java/com/keuin/kbackupfabric/ConfigInitializer.java b/src/main/java/com/keuin/kbackupfabric/ConfigInitializer.java new file mode 100644 index 0000000..770c1cd --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/ConfigInitializer.java @@ -0,0 +1,8 @@ +package com.keuin.kbackupfabric; + +/** + * Initialize classes using config from disk. + */ +public class ConfigInitializer { + +} diff --git a/src/main/java/com/keuin/kbackupfabric/KBConfiguration.java b/src/main/java/com/keuin/kbackupfabric/KBConfiguration.java new file mode 100644 index 0000000..3744430 --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/KBConfiguration.java @@ -0,0 +1,33 @@ +package com.keuin.kbackupfabric; + +/** + * Global plugin configuration. + */ +public class KBConfiguration { + // auto backup interval in seconds. Set this to a negative value to disable auto backup. + private final int autoBackupIntervalSeconds; + // name of backup created automatically. By default it is `auto-backup` + private final String autoBackupName; + // if no player has logged in since previous backup, we skip this backup + private final boolean skipAutoBackupIfNoPlayerLoggedIn; + + public KBConfiguration() { + autoBackupIntervalSeconds = -1; // disabled by default + autoBackupName = "auto-backup"; + skipAutoBackupIfNoPlayerLoggedIn = false; + } + + public KBConfiguration(int autoBackupIntervalSeconds, String autoBackupName, boolean skipAutoBackupIfNoPlayerLoggedIn) { + this.autoBackupIntervalSeconds = autoBackupIntervalSeconds; + this.autoBackupName = autoBackupName; + this.skipAutoBackupIfNoPlayerLoggedIn = skipAutoBackupIfNoPlayerLoggedIn; + } + + public int getAutoBackupIntervalSeconds() { + return autoBackupIntervalSeconds; + } + + public String getAutoBackupName() { + return autoBackupName; + } +} diff --git a/src/main/java/com/keuin/kbackupfabric/autobackup/AutoBackupScheduler.java b/src/main/java/com/keuin/kbackupfabric/autobackup/AutoBackupScheduler.java new file mode 100644 index 0000000..c35a35d --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/autobackup/AutoBackupScheduler.java @@ -0,0 +1,50 @@ +package com.keuin.kbackupfabric.autobackup; + +import java.util.Optional; +import java.util.Timer; +import java.util.TimerTask; +import java.util.logging.Logger; + +public class AutoBackupScheduler { + + private Timer timer = null; + private final Logger logger = Logger.getLogger(AutoBackupScheduler.class.getName()); + private boolean skipIfNoPlayerLoggedIn; + private final PlayerActivityTracker playerActivityTracker; + + public AutoBackupScheduler(int intervalSeconds, boolean skipIfNoPlayerLoggedIn, PlayerActivityTracker playerActivityTracker) { + this.skipIfNoPlayerLoggedIn = skipIfNoPlayerLoggedIn; + this.playerActivityTracker = playerActivityTracker; + if (intervalSeconds > 0) + setInterval(intervalSeconds); + } + + public synchronized void setInterval(int intervalSeconds) { + Optional.ofNullable(timer).ifPresent(Timer::cancel); + Timer newTimer = new Timer("AutoBackupTimer"); + newTimer.schedule(new TimerTask() { + @Override + public void run() { + toggleBackup(); + } + }, 0L, intervalSeconds * 1000L); + timer = newTimer; + } + + public void setSkipIfNoPlayerLoggedIn(boolean skipIfNoPlayerLoggedIn) { + this.skipIfNoPlayerLoggedIn = skipIfNoPlayerLoggedIn; + } + + public synchronized void stop() { + timer.cancel(); + timer = null; + } + + private void toggleBackup() { + if (playerActivityTracker.getCheckpoint() || !skipIfNoPlayerLoggedIn) { + logger.info("Interval backup event is triggered."); + // TODO: perform a backup + } + } + +} diff --git a/src/main/java/com/keuin/kbackupfabric/autobackup/PlayerActivityTracker.java b/src/main/java/com/keuin/kbackupfabric/autobackup/PlayerActivityTracker.java new file mode 100644 index 0000000..ef2b85e --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/autobackup/PlayerActivityTracker.java @@ -0,0 +1,15 @@ +package com.keuin.kbackupfabric.autobackup; + +public interface PlayerActivityTracker { + /** + * Update the checkpoint, return accumulated result. + * + * @return if there is at least one player logged in since last checkpoint. + */ + boolean getCheckpoint(); + + /** + * Mark dirty. In the next checkpoint, the backup will be performed. + */ + void setCheckpoint(); +} diff --git a/src/main/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollectionIterator.java b/src/main/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollectionIterator.java index 248d36d..7e523bc 100644 --- a/src/main/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollectionIterator.java +++ b/src/main/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollectionIterator.java @@ -6,9 +6,6 @@ import java.util.List; import java.util.NoSuchElementException; public class ObjectCollectionIterator implements Iterator { - - // TODO: test this - private Iterator currentIterator; private final List cols = new LinkedList<>(); diff --git a/src/main/java/com/keuin/kbackupfabric/backup/incremental/identifier/SingleHashIdentifier.java b/src/main/java/com/keuin/kbackupfabric/backup/incremental/identifier/SingleHashIdentifier.java index 9fd61c8..b3afa7f 100644 --- a/src/main/java/com/keuin/kbackupfabric/backup/incremental/identifier/SingleHashIdentifier.java +++ b/src/main/java/com/keuin/kbackupfabric/backup/incremental/identifier/SingleHashIdentifier.java @@ -37,11 +37,11 @@ public abstract class SingleHashIdentifier implements ObjectIdentifier { } @Override - public boolean equals(Object obj) { - if (!(obj instanceof SingleHashIdentifier)) { - return false; - } - return Arrays.equals(hash, ((SingleHashIdentifier) obj).hash); + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SingleHashIdentifier that = (SingleHashIdentifier) o; + return Arrays.equals(hash, that.hash) && type.equals(that.type); } @Override diff --git a/src/main/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManager.java b/src/main/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManager.java index 0b15a84..18e0e58 100644 --- a/src/main/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManager.java +++ b/src/main/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManager.java @@ -3,8 +3,10 @@ package com.keuin.kbackupfabric.backup.incremental.manager; import com.keuin.kbackupfabric.backup.incremental.ObjectCollection2; import com.keuin.kbackupfabric.backup.incremental.ObjectCollectionIterator; import com.keuin.kbackupfabric.backup.incremental.ObjectElement; +import com.keuin.kbackupfabric.backup.incremental.identifier.ObjectIdentifier; import com.keuin.kbackupfabric.util.FilesystemUtil; import com.keuin.kbackupfabric.util.PrintUtil; +import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import java.io.File; @@ -31,7 +33,34 @@ public class IncrementalBackupStorageManager { } /** - * Add a object collection to storage base. + * Check whether the storage contains a copy of file with given identifier. + * + * @param identifier the identifier. + * @return whether the file exists. + */ + public boolean contains(@NotNull ObjectIdentifier identifier) { + Objects.requireNonNull(identifier); + return new File(backupStorageBase.toFile(), identifier.getIdentification()).isFile(); + } + + /** + * Check whether the storage contains all files in the given collection. + * + * @param collection the collection. + * @return whether all files exists. + */ + public boolean contains(@NotNull ObjectCollection2 collection) { + Objects.requireNonNull(collection); + for (ObjectCollectionIterator it = new ObjectCollectionIterator(collection); it.hasNext(); ) { + ObjectElement ele = it.next(); + if (!contains(ele.getIdentifier())) + return false; + } + return true; + } + + /** + * Add a object collection to storage base and copy files to the storage. * * @param collection the collection. * @return objects copied to the base. @@ -93,20 +122,22 @@ public class IncrementalBackupStorageManager { Iterable otherExistingCollections) { // TODO: test this Iterator iter = new ObjectCollectionIterator(collection); - Set unusedElementSet = new HashSet<>(); - iter.forEachRemaining(unusedElementSet::add); - otherExistingCollections.forEach(col -> new ObjectCollectionIterator(col).forEachRemaining(unusedElementSet::remove)); - AtomicInteger deleteCount = new AtomicInteger(); - unusedElementSet.forEach(ele -> { - File file = new File(backupStorageBase.toFile(), ele.getIdentifier().getIdentification()); + Set identifierSet = new HashSet<>(); + iter.forEachRemaining(ele -> identifierSet.add(ele.getIdentifier())); + otherExistingCollections.forEach(col -> new ObjectCollectionIterator(col) + .forEachRemaining(ele -> identifierSet.remove(ele.getIdentifier()))); + int deleteCount = 0; + for (ObjectIdentifier id : identifierSet) { + Objects.requireNonNull(id); + File file = new File(backupStorageBase.toFile(), id.getIdentification()); if (file.exists()) { if (file.delete()) - deleteCount.incrementAndGet(); + ++deleteCount; else LOGGER.warning("Failed to delete unused file " + file.getName()); } - }); - return deleteCount.get(); + } + return deleteCount; } /** diff --git a/src/main/java/com/keuin/kbackupfabric/event/handler/ConcretePlayerConnectEventHandler.java b/src/main/java/com/keuin/kbackupfabric/event/handler/ConcretePlayerConnectEventHandler.java new file mode 100644 index 0000000..171d80d --- /dev/null +++ b/src/main/java/com/keuin/kbackupfabric/event/handler/ConcretePlayerConnectEventHandler.java @@ -0,0 +1,22 @@ +package com.keuin.kbackupfabric.event.handler; + +import com.keuin.kbackupfabric.autobackup.PlayerActivityTracker; +import com.keuin.kbackupfabric.event.OnPlayerConnect; +import com.keuin.kbackupfabric.notification.DistinctNotifiable; +import com.keuin.kbackupfabric.notification.NotificationManager; +import net.minecraft.network.ClientConnection; +import net.minecraft.server.network.ServerPlayerEntity; + +public class ConcretePlayerConnectEventHandler implements OnPlayerConnect.PlayerConnectEventCallback { + private final PlayerActivityTracker playerActivityTracker; + + public ConcretePlayerConnectEventHandler(PlayerActivityTracker playerActivityTracker) { + this.playerActivityTracker = playerActivityTracker; + } + + @Override + public void onPlayerConnect(ClientConnection connection, ServerPlayerEntity player) { + playerActivityTracker.setCheckpoint(); + NotificationManager.INSTANCE.notifyPlayer(DistinctNotifiable.fromServerPlayerEntity(player)); + } +} diff --git a/src/test/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollectionIteratorTest.java b/src/test/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollectionIteratorTest.java new file mode 100644 index 0000000..8404eb1 --- /dev/null +++ b/src/test/java/com/keuin/kbackupfabric/backup/incremental/ObjectCollectionIteratorTest.java @@ -0,0 +1,49 @@ +package com.keuin.kbackupfabric.backup.incremental; + +import com.keuin.kbackupfabric.backup.incremental.identifier.FileIdentifierProvider; +import com.keuin.kbackupfabric.backup.incremental.identifier.ObjectIdentifier; +import com.keuin.kbackupfabric.backup.incremental.identifier.Sha256Identifier; +import org.junit.Test; + +import java.io.File; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; +import java.util.stream.Collectors; + +import static org.junit.Assert.*; + +public class ObjectCollectionIteratorTest { + @Test + public void testObjectCollectionIterator() throws IOException { + final String testRoot = "testfile/ObjectCollectionIteratorTest/col1"; + ObjectCollection2 col = + new ObjectCollectionFactory<> + (Sha256Identifier::fromFile, 1, 10) + .fromDirectory(new File(testRoot)); + ObjectCollectionIterator iter = new ObjectCollectionIterator(col); + Set idSet = new HashSet<>(); + Set nameSet = new HashSet<>(); + iter.forEachRemaining(ele -> { + idSet.add(ele.getIdentifier()); + nameSet.add(ele.getName()); + }); + assertEquals(new HashSet<>(Arrays.asList("1", "2", "3", "a", "b", "c", "d", "e", "f", "z1", "z2", "z3")), nameSet); + Set desiredIdSet = new HashSet<>(Arrays.asList( + "S2-092FCFBBCFCA3B5BE7AE1B5E58538E92C35AB273AE13664FED0D67484C8E78A6", + "S2-A2BBDB2DE53523B8099B37013F251546F3D65DBE7A0774FA41AF0A4176992FD4", + "S2-A3A5E715F0CC574A73C3F9BEBB6BC24F32FFD5B67B387244C2C909DA779A1478", + "S2-0263829989B6FD954F72BAAF2FC64BC2E2F01D692D4DE72986EA808F6E99813F", + "S2-53C234E5E8472B6AC51C1AE1CAB3FE06FAD053BEB8EBFD8977B010655BFDD3C3", + "S2-D8FF994D310BD1F7582DC285366C3292DEB6A8F2EC64A4E7D0B3004190200034", + "S2-8D74BEEC1BE996322AD76813BAFB92D40839895D6DD7EE808B17CA201EAC98BE", + "S2-4355A46B19D348DC2F57C046F8EF63D4538EBB936000F3C9EE954A27460DD865", + "S2-87428FC522803D31065E7BCE3CF03FE475096631E5E07BBD7A0FDE60C4CF25C7", + "S2-1121CFCCD5913F0A63FEC40A6FFD44EA64F9DC135C66634BA001D10BCF4302A2", + "S2-AE6C381493F88DA4351218C39BE5287541C9F9D4312A941E431EB4371BC515B7", + "S2-5E1B3B203B8D9C1FE1424420B5D56A8244880E1A7539EB7E88B035EC0257FAFE" + )); + assertEquals(desiredIdSet, idSet.stream().map(ObjectIdentifier::getIdentification).collect(Collectors.toSet())); + } +} \ No newline at end of file diff --git a/src/test/java/com/keuin/kbackupfabric/backup/incremental/identifier/SingleHashIdentifierTest.java b/src/test/java/com/keuin/kbackupfabric/backup/incremental/identifier/SingleHashIdentifierTest.java new file mode 100644 index 0000000..4e15095 --- /dev/null +++ b/src/test/java/com/keuin/kbackupfabric/backup/incremental/identifier/SingleHashIdentifierTest.java @@ -0,0 +1,35 @@ +package com.keuin.kbackupfabric.backup.incremental.identifier; + +import org.junit.Test; + +import java.io.File; +import java.io.IOException; + +import static org.junit.Assert.*; + +public class SingleHashIdentifierTest { + + @Test + public void testEquals() { + SingleHashIdentifier id1 = new ConcreteSingleHashIdentifier(new byte[]{1, 2, 3, 4}, "ahash"); + SingleHashIdentifier id2 = new ConcreteSingleHashIdentifier(new byte[]{1, 2, 3, 4}, "ahash"); + SingleHashIdentifier id3 = new ConcreteSingleHashIdentifier(new byte[]{1, 2, 3}, "ahash"); + SingleHashIdentifier id4 = new ConcreteSingleHashIdentifier(new byte[]{1, 2, 3, 4}, "a"); + assertEquals(id1, id1); + assertEquals(id1, id2); + assertNotEquals(id1, id3); + assertNotEquals(id1, id4); + assertNotEquals(id3, id4); + } + + private static class ConcreteSingleHashIdentifier extends SingleHashIdentifier { + protected ConcreteSingleHashIdentifier(byte[] hash, String type) { + super(hash, type); + } + + @Override + protected byte[] hash(File file) throws IOException { + return new byte[0]; + } + } +} \ No newline at end of file diff --git a/src/test/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManagerTest.java b/src/test/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManagerTest.java new file mode 100644 index 0000000..2a9420a --- /dev/null +++ b/src/test/java/com/keuin/kbackupfabric/backup/incremental/manager/IncrementalBackupStorageManagerTest.java @@ -0,0 +1,201 @@ +package com.keuin.kbackupfabric.backup.incremental.manager; + +import com.keuin.kbackupfabric.backup.incremental.ObjectCollection2; +import com.keuin.kbackupfabric.backup.incremental.ObjectCollectionFactory; +import com.keuin.kbackupfabric.backup.incremental.ObjectCollectionIterator; +import com.keuin.kbackupfabric.backup.incremental.identifier.ObjectIdentifier; +import com.keuin.kbackupfabric.backup.incremental.identifier.Sha256Identifier; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.*; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +public class IncrementalBackupStorageManagerTest { + + private static final String testRoot = "testfile/IncrementalBackupStorageManagerTest"; + private static final String srcRoot = testRoot + "/src"; + private static final String destRoot = testRoot + "/dest"; + + private final Map files = new HashMap<>(); + + private static void deleteDirectoryTree(Path directory) throws IOException { + if (!directory.toFile().exists()) + return; + if (!directory.toFile().isDirectory() && !directory.toFile().delete()) + throw new IOException("failed to delete file " + directory); + + Files.walkFileTree(directory, new SimpleFileVisitor() { + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException { + Files.delete(file); + return FileVisitResult.CONTINUE; + } + + @Override + public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException { + Files.delete(dir); + return FileVisitResult.CONTINUE; + } + }); + } + + private static ObjectIdentifier addFile(String fileName) throws IOException { + final File root = new File(srcRoot); + final File dest = new File(root, fileName); + final Random rnd = new Random(); + if (dest.exists()) { + deleteDirectoryTree(dest.toPath()); + } + try (FileOutputStream fos = new FileOutputStream(dest)) { + fos.write(rnd.nextInt()); + } + return Sha256Identifier.fromFile(dest); + } + + private static void randomlyDeleteFiles(int count) throws IOException { + if (count == 0) + return; + if (count < 0) + throw new IllegalArgumentException("negative count"); + int[] c = new int[]{0}; + Random rnd = new Random(); + Files.walk(new File(srcRoot).toPath(), 1).map(p -> rnd.nextBoolean() ? p : null) + .filter(Objects::nonNull).forEach(p -> { + if (c[0]++ != count) { + if (!p.toFile().delete() && p.toFile().isFile()) + throw new RuntimeException("Failed to delete file " + p); + } + }); + } + + private static String getRandomString(int length) { + String str = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; + Random random = new Random(); + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < length; i++) { + int number = random.nextInt(62); + sb.append(str.charAt(number)); + } + return sb.toString(); + } + + @Before + public void initialize() throws IOException { + // init directory structure + final File rootFile = new File(testRoot); + deleteDirectoryTree(rootFile.toPath()); + assertTrue(rootFile.mkdir()); + assertTrue(new File(srcRoot).mkdir()); + assertTrue(new File(destRoot).mkdir()); + assertTrue(rootFile.isDirectory()); + assertTrue(new File(srcRoot).isDirectory()); + assertTrue(new File(destRoot).isDirectory()); + + // add some random files + files.clear(); + for (int i = 0; i < 100; i++) { + String fileName = getRandomString(20); + files.put(fileName, addFile(fileName)); + } + } + + @After + public void clean() throws IOException { + deleteDirectoryTree(new File(testRoot).toPath()); + } + + @Test + public void testDeleteAll() throws IOException { + ObjectCollection2 col = new ObjectCollectionFactory<>(Sha256Identifier::fromFile, + 1, 1) + .fromDirectory(new File(srcRoot)); + IncrementalBackupStorageManager manager = + new IncrementalBackupStorageManager(new File(destRoot).toPath()); + manager.addObjectCollection(col, new File(srcRoot)); + assertTrue(manager.contains(col)); + manager.deleteObjectCollection(col); + new ObjectCollectionIterator(col).forEachRemaining( + ele -> assertFalse(manager.contains(ele.getIdentifier()))); + } + + @Test + public void testDeleteNone() throws IOException { + ObjectCollection2 col = new ObjectCollectionFactory<>(Sha256Identifier::fromFile, + 1, 1) + .fromDirectory(new File(srcRoot)); + IncrementalBackupStorageManager manager = + new IncrementalBackupStorageManager(new File(destRoot).toPath()); + manager.addObjectCollection(col, new File(srcRoot)); + assertTrue(manager.contains(col)); + manager.deleteObjectCollection(col, Collections.singleton(col)); + new ObjectCollectionIterator(col).forEachRemaining( + ele -> assertTrue(manager.contains(ele.getIdentifier()))); + } + + @Test + public void testDeleteSub() throws IOException { + // create partial collection + ObjectCollection2 col = new ObjectCollectionFactory<>(Sha256Identifier::fromFile, + 1, 1) + .fromDirectory(new File(srcRoot)); + IncrementalBackupStorageManager manager = + new IncrementalBackupStorageManager(new File(destRoot).toPath()); + manager.addObjectCollection(col, new File(srcRoot)); + assertTrue(manager.contains(col)); + manager.deleteObjectCollection(col); + new ObjectCollectionIterator(col).forEachRemaining( + ele -> assertFalse(manager.contains(ele.getIdentifier()))); + + // expand + randomlyDeleteFiles(25); + for (int i = 0; i < 50; i++) { + String fileName = getRandomString(20); + files.put(fileName, addFile(fileName)); + } + ObjectCollection2 col2 = new ObjectCollectionFactory<>(Sha256Identifier::fromFile, + 1, 1) + .fromDirectory(new File(srcRoot)); + manager.addObjectCollection(col2, new File(srcRoot)); + manager.deleteObjectCollection(col, Collections.singleton(col2)); + assertTrue(manager.contains(col2)); + } + + @Test + public void testDeleteSuper() throws IOException { + // create partial collection + ObjectCollection2 col = new ObjectCollectionFactory<>(Sha256Identifier::fromFile, + 1, 1) + .fromDirectory(new File(srcRoot)); + IncrementalBackupStorageManager manager = + new IncrementalBackupStorageManager(new File(destRoot).toPath()); + manager.addObjectCollection(col, new File(srcRoot)); + assertTrue(manager.contains(col)); + manager.deleteObjectCollection(col); + new ObjectCollectionIterator(col).forEachRemaining( + ele -> assertFalse(manager.contains(ele.getIdentifier()))); + + // expand + for (int i = 0; i < 50; i++) { + String fileName = getRandomString(20); + files.put(fileName, addFile(fileName)); + } + ObjectCollection2 col2 = new ObjectCollectionFactory<>(Sha256Identifier::fromFile, + 1, 1) + .fromDirectory(new File(srcRoot)); + manager.addObjectCollection(col2, new File(srcRoot)); + manager.deleteObjectCollection(col2, Collections.singleton(col)); + assertTrue(manager.contains(col)); + } +} \ No newline at end of file -- cgit v1.2.3