Cherry-pick to fix EntryMaker issue

This commit is contained in:
Octavia Togami
2019-12-16 03:00:12 -08:00
committed by IronApollo
parent 735a37ffd0
commit ff47e6f717
29 changed files with 435 additions and 300 deletions

View File

@ -0,0 +1,131 @@
/*
* WorldEdit, a Minecraft world manipulation toolkit
* Copyright (C) sk89q <http://www.sk89q.com>
* Copyright (C) WorldEdit team and contributors
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as published by the
* Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.sk89q.worldedit.world.snapshot.experimental.fs;
import com.google.common.collect.ImmutableMap;
import com.sk89q.worldedit.world.storage.LegacyChunkStore;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.time.ZonedDateTime;
import static com.sk89q.worldedit.world.snapshot.experimental.fs.FileSystemSnapshotDatabaseTest.CHUNK_DATA;
import static com.sk89q.worldedit.world.snapshot.experimental.fs.FileSystemSnapshotDatabaseTest.CHUNK_POS;
import static com.sk89q.worldedit.world.snapshot.experimental.fs.FileSystemSnapshotDatabaseTest.FORMATTER;
import static com.sk89q.worldedit.world.snapshot.experimental.fs.FileSystemSnapshotDatabaseTest.REGION_DATA;
interface EntryMaker<T> {
EntryMaker<ZonedDateTime> TIMESTAMPED_DIR = (directory, time) -> {
Path timestampedDir = directory.resolve(time.format(FORMATTER));
Files.createDirectories(timestampedDir);
return timestampedDir;
};
EntryMaker<ZonedDateTime> TIMESTAMPED_ARCHIVE = (directory, time) -> {
Path zipFile = directory.resolve(time.format(FORMATTER) + ".zip");
try (FileSystem zipFs = FileSystems.newFileSystem(
URI.create("jar:" + zipFile.toUri() + "!/"),
ImmutableMap.of("create", "true")
)) {
TIMESTAMPED_DIR.createEntry(zipFs.getPath("/"), time);
}
return zipFile;
};
EntryMaker<String> WORLD_DIR = (directory, worldName) -> {
Path worldDir = directory.resolve(worldName);
Files.createDirectories(worldDir);
Files.createFile(worldDir.resolve("level.dat"));
Path regionFolder = worldDir.resolve("region");
Files.createDirectory(regionFolder);
Files.write(regionFolder.resolve("r.0.0.mca"), REGION_DATA);
Files.write(regionFolder.resolve("r.1.1.mcr"), REGION_DATA);
return worldDir;
};
class DimInfo {
final String worldName;
final int dim;
DimInfo(String worldName, int dim) {
this.worldName = worldName;
this.dim = dim;
}
}
EntryMaker<DimInfo> WORLD_DIM_DIR = (directory, dimInfo) -> {
Path worldDir = directory.resolve(dimInfo.worldName);
Files.createDirectories(worldDir);
Files.createFile(worldDir.resolve("level.dat"));
Path dimFolder = worldDir.resolve("DIM" + dimInfo.dim).resolve("region");
Files.createDirectories(dimFolder);
Files.write(dimFolder.resolve("r.0.0.mca"), REGION_DATA);
Files.write(dimFolder.resolve("r.1.1.mcr"), REGION_DATA);
return worldDir;
};
EntryMaker<String> WORLD_NO_REGION_DIR = (directory, worldName) -> {
Path worldDir = directory.resolve(worldName);
Files.createDirectories(worldDir);
Files.createFile(worldDir.resolve("level.dat"));
Files.write(worldDir.resolve("r.0.0.mca"), REGION_DATA);
Files.write(worldDir.resolve("r.1.1.mcr"), REGION_DATA);
return worldDir;
};
EntryMaker<String> WORLD_LEGACY_DIR = (directory, worldName) -> {
Path worldDir = directory.resolve(worldName);
Files.createDirectories(worldDir);
Files.createFile(worldDir.resolve("level.dat"));
Path chunkFile = worldDir.resolve(LegacyChunkStore.getFilename(
CHUNK_POS.toBlockVector2(), "/"
));
Files.createDirectories(chunkFile.getParent());
Files.write(chunkFile, CHUNK_DATA);
chunkFile = worldDir.resolve(LegacyChunkStore.getFilename(
CHUNK_POS.add(32, 0, 32).toBlockVector2(), "/"
));
Files.createDirectories(chunkFile.getParent());
Files.write(chunkFile, CHUNK_DATA);
return worldDir;
};
EntryMaker<String> WORLD_ARCHIVE = (directory, worldName) -> {
Path tempDir = Files.createTempDirectory("worldedit-fs-snap-db" + worldName);
Path temp = tempDir.resolve(worldName + ".zip");
try {
Files.deleteIfExists(temp);
try (FileSystem zipFs = FileSystems.newFileSystem(
URI.create("jar:" + temp.toUri() + "!/"),
ImmutableMap.of("create", "true")
)) {
WORLD_DIR.createEntry(zipFs.getPath("/"), worldName);
}
Path zipFile = directory.resolve(worldName + ".zip");
Files.copy(temp, zipFile);
return zipFile;
} finally {
Files.deleteIfExists(temp);
Files.deleteIfExists(tempDir);
}
};
Path createEntry(Path directory, T name) throws IOException;
}

View File

@ -19,8 +19,6 @@
package com.sk89q.worldedit.world.snapshot.experimental.fs;
import com.sk89q.worldedit.util.io.Closer;
import com.sk89q.worldedit.util.io.file.ArchiveDir;
import com.sk89q.worldedit.util.io.file.ArchiveNioSupport;
import com.sk89q.worldedit.world.snapshot.experimental.Snapshot;
@ -30,7 +28,6 @@ import java.net.URI;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import java.util.stream.Stream;
import static com.google.common.base.Preconditions.checkArgument;
import static java.util.stream.Collectors.toList;
@ -70,34 +67,19 @@ class FSSDContext {
String worldName = Paths.get(name).getFileName().toString();
// Without an extension
worldName = worldName.split("\\.")[0];
List<Snapshot> snapshots;
try (Stream<Snapshot> snapshotStream = db.getSnapshots(worldName)) {
snapshots = snapshotStream.collect(toList());
}
try {
assertTrue(snapshots.size() <= 1,
"Too many snapshots matched for " + worldName);
return requireSnapshot(name, snapshots.stream().findAny().orElse(null));
} catch (Throwable t) {
Closer closer = Closer.create();
snapshots.forEach(closer::register);
throw closer.rethrowAndClose(t);
}
List<Snapshot> snapshots = db.getSnapshots(worldName).collect(toList());
assertTrue(1 >= snapshots.size(),
"Too many snapshots matched for " + worldName);
return requireSnapshot(name, snapshots.stream().findAny().orElse(null));
}
Snapshot requireSnapshot(String name, @Nullable Snapshot snapshot) throws IOException {
Snapshot requireSnapshot(String name, @Nullable Snapshot snapshot) {
assertNotNull(snapshot, "No snapshot for " + name);
try {
assertEquals(name, snapshot.getInfo().getDisplayName());
} catch (Throwable t) {
Closer closer = Closer.create();
closer.register(snapshot);
throw closer.rethrowAndClose(t);
}
assertEquals(name, snapshot.getInfo().getDisplayName());
return snapshot;
}
ArchiveDir getRootOfArchive(Path archive) throws IOException {
Path getRootOfArchive(Path archive) throws IOException {
return archiveNioSupport.tryOpenAsDir(archive)
.orElseThrow(() -> new AssertionError("No archive opener for " + archive));
}

View File

@ -21,7 +21,6 @@ package com.sk89q.worldedit.world.snapshot.experimental.fs;
import com.google.common.collect.ImmutableList;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.util.io.file.ArchiveDir;
import com.sk89q.worldedit.world.DataException;
import com.sk89q.worldedit.world.snapshot.experimental.Snapshot;
import org.junit.jupiter.api.DynamicNode;
@ -30,6 +29,7 @@ import org.junit.jupiter.api.DynamicTest;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.FileSystems;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
@ -38,8 +38,8 @@ import java.util.List;
import java.util.concurrent.ThreadLocalRandom;
import java.util.stream.Stream;
import static com.sk89q.worldedit.world.snapshot.experimental.fs.FileSystemSnapshotDatabaseTest.CHUNK_POS;
import static com.sk89q.worldedit.world.snapshot.experimental.fs.FileSystemSnapshotDatabaseTest.CHUNK_TAG;
import static com.sk89q.worldedit.world.snapshot.experimental.fs.FileSystemSnapshotDatabaseTest.CHUNK_POS;
import static com.sk89q.worldedit.world.snapshot.experimental.fs.FileSystemSnapshotDatabaseTest.TIME_ONE;
import static com.sk89q.worldedit.world.snapshot.experimental.fs.FileSystemSnapshotDatabaseTest.TIME_TWO;
import static com.sk89q.worldedit.world.snapshot.experimental.fs.FileSystemSnapshotDatabaseTest.WORLD_ALPHA;
@ -102,11 +102,16 @@ enum FSSDTestType {
List<DynamicTest> getTests(FSSDContext context) throws IOException {
Path worldArchive = EntryMaker.WORLD_ARCHIVE
.createEntry(context.db.getRoot(), WORLD_ALPHA);
try (ArchiveDir rootOfArchive = context.getRootOfArchive(worldArchive)) {
Path rootOfArchive = context.getRootOfArchive(worldArchive);
try {
Files.setLastModifiedTime(
rootOfArchive.getPath(),
rootOfArchive,
FileTime.from(TIME_ONE.toInstant())
);
} finally {
if (rootOfArchive.getFileSystem() != FileSystems.getDefault()) {
rootOfArchive.getFileSystem().close();
}
}
return singleSnapTest(context, WORLD_ALPHA + ".zip", TIME_ONE);
}
@ -139,9 +144,14 @@ enum FSSDTestType {
Path root = context.db.getRoot();
Path timestampedArchive = EntryMaker.TIMESTAMPED_ARCHIVE
.createEntry(root, TIME_ONE);
try (ArchiveDir timestampedDir = context.getRootOfArchive(timestampedArchive)) {
EntryMaker.WORLD_DIR.createEntry(timestampedDir.getPath(), WORLD_ALPHA);
EntryMaker.WORLD_ARCHIVE.createEntry(timestampedDir.getPath(), WORLD_BETA);
Path timestampedDir = context.getRootOfArchive(timestampedArchive);
try {
EntryMaker.WORLD_DIR.createEntry(timestampedDir, WORLD_ALPHA);
EntryMaker.WORLD_ARCHIVE.createEntry(timestampedDir, WORLD_BETA);
} finally {
if (timestampedDir.getFileSystem() != FileSystems.getDefault()) {
timestampedDir.getFileSystem().close();
}
}
return ImmutableList.of(
dynamicContainer("world dir",
@ -251,18 +261,16 @@ enum FSSDTestType {
}
};
List<DynamicTest> singleSnapTest(FSSDContext context, String name,
private static List<DynamicTest> singleSnapTest(FSSDContext context, String name,
ZonedDateTime time) {
return ImmutableList.of(
dynamicTest("return a valid snapshot for " + name, () -> {
try (Snapshot snapshot = context.requireSnapshot(name)) {
assertValidSnapshot(time, snapshot);
}
Snapshot snapshot = context.requireSnapshot(name);
assertValidSnapshot(time, snapshot);
}),
dynamicTest("list a valid snapshot for " + name, () -> {
try (Snapshot snapshot = context.requireListsSnapshot(name)) {
assertValidSnapshot(time, snapshot);
}
Snapshot snapshot = context.requireListsSnapshot(name);
assertValidSnapshot(time, snapshot);
})
);
}

View File

@ -31,7 +31,6 @@ import com.sk89q.worldedit.util.io.file.ZipArchiveNioSupport;
import com.sk89q.worldedit.world.DataException;
import com.sk89q.worldedit.world.storage.ChunkStoreHelper;
import com.sk89q.worldedit.world.storage.McRegionReader;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.DynamicNode;
@ -77,8 +76,6 @@ class FileSystemSnapshotDatabaseTest {
.atZone(ZoneId.systemDefault());
static final ZonedDateTime TIME_TWO = TIME_ONE.minusDays(1);
private static Path TEMP_DIR;
@BeforeAll
static void setUpStatic() throws IOException, DataException {
try (InputStream in = Resources.getResource("world_region.mca.gzip").openStream();
@ -107,17 +104,10 @@ class FileSystemSnapshotDatabaseTest {
} finally {
reader.close();
}
TEMP_DIR = Files.createTempDirectory("worldedit-fs-snap-dbs");
}
@AfterAll
static void afterAll() throws IOException {
deleteTree(TEMP_DIR);
}
private static Path newTempDb() throws IOException {
return Files.createTempDirectory(TEMP_DIR, "db");
return Files.createTempDirectory("worldedit-fs-snap-db");
}
private static void deleteTree(Path root) throws IOException {
@ -185,6 +175,7 @@ class FileSystemSnapshotDatabaseTest {
try {
Path dbRoot = root.resolve("snapshots");
Files.createDirectories(dbRoot);
// we leak `root` here, but I can't see a good way to clean it up.
return type.getNamedTests(new FSSDContext(nioSupport, dbRoot));
} catch (Throwable t) {
deleteTree(root);