Introduce new SongodaYamlConfig and ConfigEntry classes

This introduces an additional abstraction layer on top of the YamlConfiguration.
This is the class that should normally be used by plugins.
This commit is contained in:
Christian Koop 2022-06-26 01:51:53 +02:00
parent 8310541b91
commit eb10b3f70a
No known key found for this signature in database
GPG Key ID: 89A8181384E010A3
7 changed files with 915 additions and 0 deletions

View File

@ -0,0 +1,235 @@
package com.songoda.core.configuration.songoda;
import com.songoda.core.compatibility.CompatibleMaterial;
import com.songoda.core.configuration.NodeCommentable;
import com.songoda.core.utils.Pair;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
public class ConfigEntry {
public final @NotNull SongodaYamlConfig config;
public final @NotNull String key;
protected @Nullable Object defaultValue;
protected @Nullable Map<Integer, Pair<@Nullable String, @Nullable Function<@Nullable Object, @Nullable Object>>> upgradeStepsForVersion;
public ConfigEntry(@NotNull SongodaYamlConfig config, @NotNull String key) {
this(config, key, null);
}
public ConfigEntry(@NotNull SongodaYamlConfig config, @NotNull String key, @Nullable Object defaultValue) {
this.config = Objects.requireNonNull(config);
this.key = Objects.requireNonNull(key);
this.defaultValue = defaultValue;
if (get() == null) {
set(this.defaultValue);
}
this.config.registerConfigEntry(this);
}
public @Nullable Object getDefaultValue() {
return this.defaultValue;
}
public void setDefaultValue(@Nullable Object defaultValue) {
this.defaultValue = defaultValue;
}
/**
* @see #withComment(Supplier)
*/
public ConfigEntry withComment(String comment) {
return this.withComment(() -> comment);
}
/**
* @see NodeCommentable#setNodeComment(String, Supplier)
*/
public ConfigEntry withComment(Supplier<String> comment) {
((NodeCommentable) this.config).setNodeComment(this.key, comment);
return this;
}
/**
* @see #withUpgradeStep(int, String, Function)
*/
public ConfigEntry withUpgradeStep(int version, @NotNull String keyInGivenVersion) {
return withUpgradeStep(version, keyInGivenVersion, null);
}
/**
* @param version The version to upgrade from (e.g. 1 for the upgrade from 1 to 2)
* @param keyInGivenVersion The old key in the given version or null if it didn't change
* @param valueConverter A function that converts the old version's value to a new one, or null if it didn't change
*/
@Contract("_, null, null -> fail")
public ConfigEntry withUpgradeStep(int version, @Nullable String keyInGivenVersion, @Nullable Function<Object, Object> valueConverter) {
if (keyInGivenVersion == null && valueConverter == null) {
throw new IllegalArgumentException("You must provide either a key or a value converter");
}
if (this.upgradeStepsForVersion == null) {
this.upgradeStepsForVersion = new HashMap<>(1);
}
this.upgradeStepsForVersion.put(version, new Pair<>(keyInGivenVersion, valueConverter));
return this;
}
/**
* @see SongodaYamlConfig#has(String)
*/
public boolean has() {
return this.config.has(this.key);
}
/**
* @see SongodaYamlConfig#set(String, Object)
*/
public Object set(@Nullable Object value) {
// TODO: Test what happens if the value is an enum (CompatibleMaterial)
return this.config.set(this.key, value);
}
/**
* @see SongodaYamlConfig#get(String)
*/
public @Nullable Object get() {
return this.config.get(this.key);
}
/**
* @see SongodaYamlConfig#getOr(String, Object)
*/
public @Nullable Object getOr(@Nullable Object fallbackValue) {
return this.config.getOr(this.key, fallbackValue);
}
public @Nullable String getString() {
Object value = get();
return value != null ? value.toString() : null;
}
public @Nullable String getString(String fallbackValue) {
Object value = get();
return value == null ? fallbackValue : value.toString();
}
/**
* @see #getInt(int)
*/
public int getInt() {
return getInt(0);
}
/**
* Returns the values parsed as an integer.<br>
* If it is a floating point number, it will be rounded down.
*
* @see Double#valueOf(String)
*/
public int getInt(int fallbackValue) {
String value = getString();
if (value == null) {
return fallbackValue;
}
return Double.valueOf(value).intValue();
}
/**
* @see #getDouble(double)
*/
public double getDouble() {
return getDouble(0);
}
/**
* Returns the values parsed as a double.
*
* @see Double#parseDouble(String)
*/
public double getDouble(double fallbackValue) {
String value = getString();
if (value == null) {
return fallbackValue;
}
return Double.parseDouble(value);
}
/**
* @see #getBoolean(boolean)
*/
public boolean getBoolean() {
return getBoolean(false);
}
/**
* Returns the values parsed as a boolean.
*
* @see Boolean#parseBoolean(String)
*/
public boolean getBoolean(boolean fallbackValue) {
String value = getString();
if (value == null) {
return fallbackValue;
}
return Boolean.parseBoolean(value);
}
/**
* @see #getMaterial(CompatibleMaterial)
*/
public CompatibleMaterial getMaterial() {
return getMaterial(null);
}
/**
* @see CompatibleMaterial#getMaterial(String)
*/
public @Nullable CompatibleMaterial getMaterial(@Nullable CompatibleMaterial defaultValue) {
String value = getString();
if (value == null) {
return defaultValue;
}
return CompatibleMaterial.getMaterial(value);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
ConfigEntry that = (ConfigEntry) o;
return this.config.equals(that.config) &&
this.key.equals(that.key) &&
Objects.equals(this.defaultValue, that.defaultValue) &&
Objects.equals(this.upgradeStepsForVersion, that.upgradeStepsForVersion);
}
@Override
public int hashCode() {
return Objects.hash(this.config, this.key, this.defaultValue, this.upgradeStepsForVersion);
}
}

View File

@ -0,0 +1,240 @@
package com.songoda.core.configuration.songoda;
import com.songoda.core.configuration.yaml.YamlConfiguration;
import com.songoda.core.utils.Pair;
import org.bukkit.plugin.java.JavaPlugin;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Reader;
import java.io.Writer;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.logging.Level;
import java.util.logging.Logger;
public class SongodaYamlConfig extends YamlConfiguration {
protected final String cannotCreateBackupCopyExceptionPrefix = "Unable to create backup copy of config file: ";
protected final @NotNull File file;
protected final @NotNull Logger logger;
private int targetVersion;
private ConfigEntry versionEntry;
protected final Map<String, ConfigEntry> configEntries = new LinkedHashMap<>(0);
public SongodaYamlConfig(@NotNull JavaPlugin plugin, @NotNull File file) {
this(file, plugin.getLogger());
}
public SongodaYamlConfig(@NotNull JavaPlugin plugin, @NotNull String fileName) {
this(new File(plugin.getDataFolder(), fileName), plugin.getLogger());
}
public SongodaYamlConfig(@NotNull File file) {
this(file, null);
}
public SongodaYamlConfig(@NotNull File file, @Nullable Logger logger) {
super();
this.file = Objects.requireNonNull(file);
if (logger == null) {
logger = Logger.getLogger(getClass().getName());
}
this.logger = logger;
}
/**
* Calls {@link #load()} and then {@link #save()}.<br>
* <br>
* As this is intered to keep the {@link org.bukkit.plugin.java.JavaPlugin#onEnable()} method clean,
* it catches all exceptions and logs them instead.<br>
* <br>
* If this method returns false, the plugins should be disabled.
*
* @return true if the load and save were successful, false if an exception was thrown.
*
* @see #save()
* @see #load()
*/
public boolean init() {
try {
this.load();
this.save();
return true;
} catch (IOException e) {
this.logger.log(Level.FINER, "Failed to load config file: " + this.file.getPath(), e);
}
return false;
}
protected void registerConfigEntry(ConfigEntry entry) {
this.configEntries.put(entry.key, entry);
}
public void unregisterConfigEntry(ConfigEntry entry) {
unregisterConfigEntry(entry.key);
}
public void unregisterConfigEntry(String key) {
this.configEntries.remove(key);
}
public SongodaYamlConfig withVersion(int version) {
return withVersion("version", version, () -> "Don't touch this it's used to track the version of the config.");
}
public SongodaYamlConfig withVersion(@NotNull String key, int version, @Nullable Supplier<String> comment) {
if (version < 0) {
throw new IllegalArgumentException("Version must be positive");
}
if (this.versionEntry != null) {
this.versionEntry.set(null);
}
this.targetVersion = version;
this.versionEntry = new ConfigEntry(this, key, 0);
this.versionEntry.withComment(comment);
this.versionEntry.set(this.targetVersion);
return this;
}
public void load() throws IOException {
try (Reader reader = new FileReader(this.file)) {
load(reader);
} catch (FileNotFoundException ignore) {
} catch (IOException e) {
throw new IOException("Unable to load '" + this.file.getPath() + "'", e);
}
}
public void save() throws IOException {
try (Writer writer = new FileWriter(this.file)) {
super.save(writer);
} catch (IOException e) {
throw new IOException("Unable to save '" + this.file.getPath() + "'", e);
}
}
@Override
public void load(Reader reader) {
super.load(reader);
// The interface does not allow to throw an exception, so we log it instead.
try {
upgradeOldConfigVersion();
} catch (IOException ex) {
if (ex.getMessage().startsWith(this.cannotCreateBackupCopyExceptionPrefix)) {
// Failed to create backup copy, but we can still continue.
this.logger.log(Level.SEVERE, null, ex);
} else {
// This is a real unexpected exception, so we rethrow it.
throw new IllegalStateException(ex);
}
}
for (ConfigEntry entry : this.configEntries.values()) {
if (entry.get() == null && entry.getDefaultValue() != null) {
entry.set(entry.getDefaultValue());
}
}
}
/**
* @return false, if no config version is set or no upgrade is needed
*/
protected boolean upgradeOldConfigVersion() throws IOException {
if (this.versionEntry == null) {
return false;
}
if (this.versionEntry.getInt() > this.targetVersion) {
throw new IllegalStateException("Cannot upgrade a config version that is higher than the target version");
}
if (this.versionEntry.getInt() == this.targetVersion) {
return false;
}
createBackupCopyFile();
while (this.versionEntry.getInt() < this.targetVersion) {
upgradeOldConfigVersionByOne();
}
return true;
}
protected void upgradeOldConfigVersionByOne() {
int currentVersion = this.versionEntry.getInt();
int targetVersion = currentVersion + 1;
if (targetVersion > this.targetVersion) {
throw new IllegalStateException("Cannot upgrade a config version that is higher than the target version");
}
for (ConfigEntry entry : this.configEntries.values()) {
if (entry.upgradeStepsForVersion == null) {
continue;
}
Pair<@Nullable String, @Nullable Function<Object, Object>> upgradeStep = entry.upgradeStepsForVersion.get(currentVersion);
if (upgradeStep == null) {
continue;
}
String oldEntryKey = upgradeStep.getFirst();
if (oldEntryKey == null) {
oldEntryKey = entry.key;
}
Object newValue = get(oldEntryKey);
if (upgradeStep.getSecond() != null) {
newValue = upgradeStep.getSecond().apply(newValue);
}
set(oldEntryKey, null);
entry.set(newValue);
}
this.versionEntry.set(targetVersion);
}
protected void createBackupCopyFile() throws IOException {
if (!this.file.exists()) {
return;
}
try {
Path targetPath = this.file.toPath().resolveSibling(this.file.getPath() + ".backup" + System.currentTimeMillis());
Files.copy(
this.file.toPath(),
targetPath,
StandardCopyOption.REPLACE_EXISTING
);
this.logger.warning("Created backup copy of config file '" + this.file.getPath() + "' to '" + targetPath + "'");
} catch (IOException ex) {
throw new IOException(this.cannotCreateBackupCopyExceptionPrefix + this.file.getPath(), ex);
}
}
}

View File

@ -28,6 +28,8 @@ import java.util.Objects;
import java.util.Set;
import java.util.function.Supplier;
// TODO: Allow registering own custom value converter (e.g. Bukkit-Location to Map and back)
// + move the huge block from #set into such a converter and register it by default
public class YamlConfiguration implements IConfiguration, HeaderCommentable, NodeCommentable {
protected final @NotNull Yaml yaml;
protected final @NotNull DumperOptions yamlDumperOptions;

View File

@ -0,0 +1,19 @@
package com.songoda.core.utils;
public class Pair<T, U> {
private final T first;
private final U second;
public Pair(T first, U second) {
this.first = first;
this.second = second;
}
public T getFirst() {
return this.first;
}
public U getSecond() {
return this.second;
}
}

View File

@ -0,0 +1,192 @@
package com.songoda.core.configuration.songoda;
import com.songoda.core.compatibility.CompatibleMaterial;
import org.junit.jupiter.api.Test;
import java.io.File;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNotEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ConfigEntryTest {
@Test
void testGetDefaultValue() {
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
ConfigEntry entry = new ConfigEntry(cfg, "key", "value");
assertEquals("value", entry.getDefaultValue());
entry.setDefaultValue("new-value");
assertEquals("new-value", entry.getDefaultValue());
}
@Test
void testGetOr() {
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
ConfigEntry entry = new ConfigEntry(cfg, "key", "value");
assertEquals("value", entry.getOr("invalid"));
entry.set(null);
assertEquals("invalid", entry.getOr("invalid"));
}
@Test
void testGetString() {
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
ConfigEntry entry = new ConfigEntry(cfg, "key");
entry.set("value");
assertEquals("value", entry.getString());
entry.set("new-value");
assertEquals("new-value", entry.getString());
entry.set(null);
assertNull(entry.getString());
assertNull(entry.getString(null));
assertEquals("12", entry.getString("12"));
entry.set(10.5);
assertEquals("10.5", entry.getString());
entry.set(true);
assertEquals("true", entry.getString());
entry.set(CompatibleMaterial.STONE);
assertEquals("STONE", entry.getString());
}
@Test
void testGetInt() {
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
ConfigEntry entry = new ConfigEntry(cfg, "key");
entry.set(1.0);
assertEquals(1, entry.getInt());
entry.set("1.5");
assertEquals(1, entry.getInt());
entry.set("10");
assertEquals(10.0, entry.getInt());
entry.set("10,0");
assertThrows(NumberFormatException.class, entry::getInt);
entry.set(null);
assertEquals(0, entry.getInt());
assertEquals(11, entry.getInt(11));
}
@Test
void testGetDouble() {
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
ConfigEntry entry = new ConfigEntry(cfg, "key");
entry.set(1.0);
assertEquals(1.0, entry.getDouble());
entry.set("1.5");
assertEquals(1.5, entry.getDouble());
entry.set("10");
assertEquals(10.0, entry.getDouble());
entry.set("10,0");
assertThrows(NumberFormatException.class, entry::getDouble);
entry.set(null);
assertEquals(0.0, entry.getDouble());
assertEquals(11.5, entry.getDouble(11.5));
}
@Test
void testGetBoolean() {
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
ConfigEntry entry = new ConfigEntry(cfg, "key");
entry.set(false);
assertFalse(entry.getBoolean());
entry.set("false");
assertFalse(entry.getBoolean());
entry.set("invalid");
assertFalse(entry.getBoolean());
entry.set(1);
assertFalse(entry.getBoolean());
entry.set(true);
assertTrue(entry.getBoolean());
entry.set("true");
assertTrue(entry.getBoolean());
entry.set(null);
assertFalse(entry.getBoolean());
assertTrue(entry.getBoolean(true));
}
@Test
void testGetMaterial() {
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
ConfigEntry entry = new ConfigEntry(cfg, "key");
entry.set("LOG");
assertEquals(CompatibleMaterial.BIRCH_LOG, entry.getMaterial());
entry.set("OAK_LOG");
assertEquals(CompatibleMaterial.OAK_LOG, entry.getMaterial());
entry.set("10");
assertNull(entry.getMaterial());
entry.set(null);
assertNull(entry.getMaterial());
assertEquals(CompatibleMaterial.ACACIA_BOAT, entry.getMaterial(CompatibleMaterial.ACACIA_BOAT));
}
@Test
void testInvalidWithUpgradeNull() {
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
ConfigEntry entry = new ConfigEntry(cfg, "key", "value");
assertThrows(IllegalArgumentException.class, () -> entry.withUpgradeStep(1, null, null));
}
@Test
void testEqualsAndHashCode() {
SongodaYamlConfig cfg = new SongodaYamlConfig(new File("ConfigEntryTest.yml"));
ConfigEntry entry = new ConfigEntry(cfg, "key", "value");
assertEquals(entry, entry);
assertEquals(entry.hashCode(), entry.hashCode());
ConfigEntry other = new ConfigEntry(cfg, "key", "value");
assertEquals(entry, other);
assertEquals(entry.hashCode(), other.hashCode());
other = new ConfigEntry(cfg, "key", "value2");
assertNotEquals(entry, other);
assertNotEquals(entry.hashCode(), other.hashCode());
other = new ConfigEntry(cfg, "key2", "value");
assertNotEquals(entry, other);
assertNotEquals(entry.hashCode(), other.hashCode());
other = new ConfigEntry(cfg, "key", "value2");
assertNotEquals(entry, other);
assertNotEquals(entry.hashCode(), other.hashCode());
other = new ConfigEntry(cfg, "key2", "value2");
assertNotEquals(entry, other);
assertNotEquals(entry.hashCode(), other.hashCode());
}
}

View File

@ -0,0 +1,93 @@
package com.songoda.core.configuration.songoda;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
class SongodaYamlConfigRoundtripTest {
Path cfg;
@BeforeEach
void setUp() throws IOException {
Path path = Files.createTempFile("SongodaYamlConfigTest", "yml");
File file = path.toFile();
file.deleteOnExit();
this.cfg = path;
}
@AfterEach
void tearDown() throws IOException {
Files.deleteIfExists(this.cfg);
}
@Test
void roundtripTest() throws IOException {
Files.write(this.cfg, ("# Don't touch this it's used to track the version of the config.\n" +
"version: 1\n" +
"messages:\n" +
" # This message is shown when the 'foo' command succeeds.\n" +
" fooSuccess: Remastered success value\n" +
"# This is the range of the 'foo' command\n").getBytes());
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile())
.withVersion(3);
ConfigEntry cmdFooSuccess = new ConfigEntry(cfg, "command.foo.success", "Default success value")
.withComment("This message is shown when the 'foo' command succeeds.")
.withUpgradeStep(1, "messages.fooSuccess");
ConfigEntry range = new ConfigEntry(cfg, "range")
.withComment("This is the range of the 'foo' command")
.withUpgradeStep(1, null, o -> {
if (o == null) {
return 10;
}
return o;
})
.withUpgradeStep(2, null, o -> o + " blocks");
ConfigEntry incrementer = new ConfigEntry(cfg, "incrementer", 0)
.withComment("This is the incrementer of the 'foo' command")
.withUpgradeStep(1, null, o -> {
if (o == null) {
return null;
}
return (int) o + 1;
})
.withUpgradeStep(3, null, (o) -> "text");
assertTrue(cfg.init());
assertNull(cfg.get("messages.fooSuccess"));
assertEquals("Remastered success value", cfg.get("command.foo.success"));
assertEquals("Remastered success value", cmdFooSuccess.get());
assertTrue(cmdFooSuccess.has());
assertTrue(range.has());
assertEquals(cfg.get("range"), range.get());
assertTrue(incrementer.has());
assertEquals(cfg.get("incrementer"), incrementer.get());
assertEquals("# Don't touch this it's used to track the version of the config.\n" +
"version: 3\n" +
"command:\n" +
" foo:\n" +
" # This message is shown when the 'foo' command succeeds.\n" +
" success: Remastered success value\n" +
"# This is the range of the 'foo' command\n" +
"range: 10 blocks\n" +
"# This is the incrementer of the 'foo' command\n" +
"incrementer: 0\n", new String(Files.readAllBytes(this.cfg)));
}
}

View File

@ -0,0 +1,134 @@
package com.songoda.core.configuration.songoda;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.io.File;
import java.io.IOException;
import java.io.StringReader;
import java.nio.file.Files;
import java.nio.file.Path;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertNull;
class SongodaYamlConfigTest {
Path cfg;
@BeforeEach
void setUp() throws IOException {
this.cfg = createTmpFile();
}
@AfterEach
void tearDown() throws IOException {
Files.deleteIfExists(this.cfg);
}
@Test
void testLoad() throws IOException {
Files.write(this.cfg, "test-key: foo\n".getBytes());
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
cfg.set("test-key", "bar");
cfg.load();
assertEquals("foo", cfg.get("test-key"));
}
@Test
void testSave() throws IOException {
Files.write(this.cfg, "test-key: foo\n".getBytes());
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
cfg.set("test-key", "bar");
cfg.save();
assertEquals("test-key: bar\n", new String(Files.readAllBytes(this.cfg)));
}
@Test
void testWithVersion() throws IOException {
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
cfg.withVersion("version-key", 1, null);
assertEquals(1, cfg.get("version-key"));
cfg.save();
assertEquals("version-key: 1\n", new String(Files.readAllBytes(this.cfg)));
cfg.withVersion(2);
assertEquals(2, cfg.get("version"));
assertNull(cfg.get("version-key"));
cfg.save();
assertEquals(
"# Don't touch this it's used to track the version of the config.\n" +
"version: 2\n",
new String(Files.readAllBytes(this.cfg))
);
}
@Test
void testWithNegativeVersion() {
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
Assertions.assertThrows(IllegalArgumentException.class, () -> cfg.withVersion("version-key", -1, null));
}
@Test
void testWithTooNewVersion() {
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile())
.withVersion(1);
Assertions.assertThrows(IllegalStateException.class, () -> cfg.load(new StringReader("version: 10\n")));
}
@Test
void testWithUpToDateVersion() throws IOException {
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile())
.withVersion(2);
assertFalse(cfg.upgradeOldConfigVersion());
}
@Test
void testWithKeyWithoutConfigEntry() throws IOException {
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
cfg.set("test-key", "foo");
cfg.load();
assertNull(cfg.get("test-key"));
cfg.set("test-key", "foo");
assertEquals("foo", cfg.get("test-key"));
cfg.save();
cfg.load();
assertEquals("foo", cfg.get("test-key"));
assertEquals(1, cfg.getKeys("").size());
}
@Test
void testDefaultValueAppliedAfterLoadNullValue() throws IOException {
SongodaYamlConfig cfg = new SongodaYamlConfig(this.cfg.toFile());
ConfigEntry entry = new ConfigEntry(cfg, "key", "value");
cfg.init();
assertEquals("value", entry.get());
}
private Path createTmpFile() throws IOException {
Path path = Files.createTempFile("SongodaYamlConfigTest", "yml");
File file = path.toFile();
file.deleteOnExit();
return path;
}
}