Fix config lateinit errors and fix testing
This commit is contained in:
parent
5e39b6ef57
commit
529fef1c5f
8 changed files with 81 additions and 79 deletions
|
@ -28,10 +28,13 @@ import org.bukkit.command.Command
|
||||||
import org.bukkit.command.CommandSender
|
import org.bukkit.command.CommandSender
|
||||||
import org.bukkit.command.ConsoleCommandSender
|
import org.bukkit.command.ConsoleCommandSender
|
||||||
import org.bukkit.entity.Player
|
import org.bukkit.entity.Player
|
||||||
|
import org.bukkit.plugin.PluginDescriptionFile
|
||||||
|
import org.bukkit.plugin.java.JavaPluginLoader
|
||||||
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Supplier
|
import java.util.function.Supplier
|
||||||
|
|
||||||
class MainPlugin : ButtonPlugin() {
|
class MainPlugin : ButtonPlugin {
|
||||||
private var economy: Economy? = null
|
private var economy: Economy? = null
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -62,6 +65,10 @@ class MainPlugin : ButtonPlugin() {
|
||||||
*/ // TODO: Combine the channel access test with command permissions (with a generic implementation)
|
*/ // TODO: Combine the channel access test with command permissions (with a generic implementation)
|
||||||
val externalPlayerPermissionGroup get() = iConfig.getData("externalPlayerPermissionGroup", "default")
|
val externalPlayerPermissionGroup get() = iConfig.getData("externalPlayerPermissionGroup", "default")
|
||||||
|
|
||||||
|
constructor() : super()
|
||||||
|
constructor(loader: JavaPluginLoader, description: PluginDescriptionFile, dataFolder: File, file: File) : super(loader, description, dataFolder, file)
|
||||||
|
|
||||||
|
|
||||||
public override fun pluginEnable() {
|
public override fun pluginEnable() {
|
||||||
instance = this
|
instance = this
|
||||||
val pdf = description
|
val pdf = description
|
||||||
|
|
|
@ -1,50 +1,21 @@
|
||||||
package buttondevteam.core;
|
package buttondevteam.core
|
||||||
|
|
||||||
import buttondevteam.core.component.channel.Channel;
|
import be.seeseemelk.mockbukkit.MockBukkit
|
||||||
import buttondevteam.core.component.channel.ChannelComponent;
|
import buttondevteam.core.component.channel.Channel
|
||||||
import buttondevteam.lib.ChromaUtils;
|
import buttondevteam.core.component.channel.ChannelComponent
|
||||||
import buttondevteam.lib.architecture.Component;
|
import buttondevteam.lib.ChromaUtils.isTest
|
||||||
import buttondevteam.lib.chat.Color;
|
import buttondevteam.lib.architecture.Component.Companion.registerComponent
|
||||||
import buttondevteam.lib.chat.TBMCChatAPI;
|
import buttondevteam.lib.chat.Color
|
||||||
import org.bukkit.Bukkit;
|
import buttondevteam.lib.chat.TBMCChatAPI.registerChatChannel
|
||||||
import org.bukkit.Server;
|
import org.bukkit.ChatColor
|
||||||
import org.bukkit.plugin.PluginManager;
|
|
||||||
import org.bukkit.plugin.java.JavaPlugin;
|
|
||||||
import org.bukkit.scheduler.BukkitScheduler;
|
|
||||||
import org.mockito.Mockito;
|
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
|
||||||
import org.mockito.stubbing.Answer;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
@Deprecated("Use MockBukkit")
|
||||||
import java.util.Collections;
|
object TestPrepare {
|
||||||
import java.util.logging.Logger;
|
@JvmStatic
|
||||||
|
fun prepareServer() {
|
||||||
public class TestPrepare {
|
isTest = true //Needs to be in a separate class because of the potential lack of Mockito
|
||||||
|
MockBukkit.mock()
|
||||||
public static void PrepareServer() {
|
registerComponent(MockBukkit.load(MainPlugin::class.java), ChannelComponent())
|
||||||
ChromaUtils.setTest(true); //Needs to be in a separate class because of the potential lack of Mockito
|
registerChatChannel(Channel("${ChatColor.WHITE}g${ChatColor.WHITE}", Color.White, "g", null).also { Channel.globalChat = it })
|
||||||
Bukkit.setServer(Mockito.mock(Server.class, new Answer<Object>() {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object answer(InvocationOnMock invocation) {
|
|
||||||
if (returns(invocation, String.class))
|
|
||||||
return "test";
|
|
||||||
if (returns(invocation, Logger.class))
|
|
||||||
return Logger.getAnonymousLogger();
|
|
||||||
if (returns(invocation, PluginManager.class))
|
|
||||||
return Mockito.mock(PluginManager.class);
|
|
||||||
if (returns(invocation, Collection.class))
|
|
||||||
return Collections.EMPTY_LIST;
|
|
||||||
if (returns(invocation, BukkitScheduler.class))
|
|
||||||
return Mockito.mock(BukkitScheduler.class);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean returns(InvocationOnMock invocation, Class<?> cl) {
|
|
||||||
return cl.isAssignableFrom(invocation.getMethod().getReturnType());
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
Component.registerComponent(Mockito.mock(JavaPlugin.class), new ChannelComponent());
|
|
||||||
TBMCChatAPI.registerChatChannel(Channel.globalChat = new Channel("${ChatColor.WHITE}g${ChatColor.WHITE}", Color.White, "g", null));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -70,6 +70,17 @@ object ChromaUtils {
|
||||||
} else what.get()
|
} else what.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Log a warning message if the plugin is initialized. If not, just print a regular message.
|
||||||
|
*/
|
||||||
|
fun logWarn(message: String) {
|
||||||
|
if (MainPlugin.isInitialized) {
|
||||||
|
MainPlugin.instance.logger.warning(message)
|
||||||
|
} else {
|
||||||
|
println(message)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns true while unit testing.
|
* Returns true while unit testing.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -8,14 +8,16 @@ import buttondevteam.lib.chat.ICommand2MC
|
||||||
import org.bukkit.configuration.InvalidConfigurationException
|
import org.bukkit.configuration.InvalidConfigurationException
|
||||||
import org.bukkit.configuration.file.FileConfiguration
|
import org.bukkit.configuration.file.FileConfiguration
|
||||||
import org.bukkit.configuration.file.YamlConfiguration
|
import org.bukkit.configuration.file.YamlConfiguration
|
||||||
|
import org.bukkit.plugin.PluginDescriptionFile
|
||||||
import org.bukkit.plugin.java.JavaPlugin
|
import org.bukkit.plugin.java.JavaPlugin
|
||||||
|
import org.bukkit.plugin.java.JavaPluginLoader
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.function.Consumer
|
import java.util.function.Consumer
|
||||||
|
|
||||||
@HasConfig(global = true)
|
@HasConfig(global = true)
|
||||||
abstract class ButtonPlugin : JavaPlugin() {
|
abstract class ButtonPlugin : JavaPlugin {
|
||||||
protected val iConfig = IHaveConfig(::saveConfig, section)
|
protected val iConfig = IHaveConfig(::saveConfig, section)
|
||||||
private var yaml: YamlConfiguration? = null
|
private var yaml: YamlConfiguration? = null
|
||||||
|
|
||||||
|
@ -31,6 +33,11 @@ abstract class ButtonPlugin : JavaPlugin() {
|
||||||
* Used to unregister components in the right order - and to reload configs
|
* Used to unregister components in the right order - and to reload configs
|
||||||
*/
|
*/
|
||||||
val componentStack = Stack<Component<*>>()
|
val componentStack = Stack<Component<*>>()
|
||||||
|
|
||||||
|
// Support testing with the protected constructor (MockBukkit)
|
||||||
|
constructor() : super()
|
||||||
|
protected constructor(loader: JavaPluginLoader, description: PluginDescriptionFile, dataFolder: File, file: File) : super(loader, description, dataFolder, file)
|
||||||
|
|
||||||
protected abstract fun pluginEnable()
|
protected abstract fun pluginEnable()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -19,7 +19,7 @@ import java.util.stream.Collectors
|
||||||
abstract class Component<TP : JavaPlugin> {
|
abstract class Component<TP : JavaPlugin> {
|
||||||
var isEnabled = false
|
var isEnabled = false
|
||||||
|
|
||||||
lateinit var config: IHaveConfig
|
var config: IHaveConfig = IHaveConfig({ logWarn("Attempted to save config with null section!") }, null)
|
||||||
private set
|
private set
|
||||||
lateinit var plugin: TP
|
lateinit var plugin: TP
|
||||||
private set
|
private set
|
||||||
|
@ -100,7 +100,11 @@ abstract class Component<TP : JavaPlugin> {
|
||||||
* @return A map containing configs
|
* @return A map containing configs
|
||||||
*/
|
*/
|
||||||
fun getConfigMap(key: String, defaultProvider: Map<String, Consumer<IHaveConfig>>): Map<String, IHaveConfig> {
|
fun getConfigMap(key: String, defaultProvider: Map<String, Consumer<IHaveConfig>>): Map<String, IHaveConfig> {
|
||||||
val c: ConfigurationSection = config.config
|
val c: ConfigurationSection? = config.config
|
||||||
|
if (c == null) {
|
||||||
|
logWarn("Config section is null when getting config map")
|
||||||
|
return defaultProvider.mapValues { kv -> IHaveConfig(plugin::saveConfig, null).also { kv.value.accept(it) } }
|
||||||
|
}
|
||||||
val cs = c.getConfigurationSection(key) ?: c.createSection(key)
|
val cs = c.getConfigurationSection(key) ?: c.createSection(key)
|
||||||
val res = cs.getValues(false).entries.stream()
|
val res = cs.getValues(false).entries.stream()
|
||||||
.filter { (_, value) -> value is ConfigurationSection }
|
.filter { (_, value) -> value is ConfigurationSection }
|
||||||
|
@ -109,11 +113,7 @@ abstract class Component<TP : JavaPlugin> {
|
||||||
{ (_, value) -> IHaveConfig(plugin::saveConfig, value as ConfigurationSection) }
|
{ (_, value) -> IHaveConfig(plugin::saveConfig, value as ConfigurationSection) }
|
||||||
))
|
))
|
||||||
if (res.isEmpty()) {
|
if (res.isEmpty()) {
|
||||||
for ((key1, value) in defaultProvider) {
|
defaultProvider.mapValuesTo(res) { kv -> IHaveConfig(plugin::saveConfig, cs.createSection(kv.key)).also { kv.value.accept(it) } }
|
||||||
val conf = IHaveConfig(plugin::saveConfig, cs.createSection(key1))
|
|
||||||
value.accept(conf)
|
|
||||||
res[key1] = conf
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
@ -121,8 +121,7 @@ abstract class Component<TP : JavaPlugin> {
|
||||||
private val className: String get() = javaClass.simpleName
|
private val className: String get() = javaClass.simpleName
|
||||||
|
|
||||||
internal fun updateConfig() {
|
internal fun updateConfig() {
|
||||||
if (!this::config.isInitialized) this.config = IHaveConfig(plugin::saveConfig, getConfigSection())
|
this.config.reload(getConfigSection(), plugin::saveConfig)
|
||||||
else this.config.reload(getConfigSection())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getConfigSection(): ConfigurationSection {
|
private fun getConfigSection(): ConfigurationSection {
|
||||||
|
|
|
@ -14,16 +14,16 @@ import java.util.function.Function
|
||||||
* Use [Component.config] or [ButtonPlugin.iConfig] then [IHaveConfig.getData] to get an instance.
|
* Use [Component.config] or [ButtonPlugin.iConfig] then [IHaveConfig.getData] to get an instance.
|
||||||
*
|
*
|
||||||
* **Note:** The instance can become outdated if the config is reloaded.
|
* **Note:** The instance can become outdated if the config is reloaded.
|
||||||
* @param config May be null for testing
|
* @param config The config object to use for the whole file
|
||||||
* @param path The path to the config value
|
* @param path The path to the config value
|
||||||
* @param primitiveDef The default value, as stored in the config. Non-nullable as it needs to be saved to the config
|
* @param primitiveDef The default value, as stored in the config. Non-nullable as it needs to be saved sto the config
|
||||||
* @param getter Function to convert primtive types to [T]. The parameter is of a primitive type as returned by [Configuration.get]
|
* @param getter Function to convert primtive types to [T]. The parameter is of a primitive type as returned by [Configuration.get]
|
||||||
* @param setter Function to convert [T] to a primitive type. The result should be a primitive type or string that can be retrieved correctly later
|
* @param setter Function to convert [T] to a primitive type. The result should be a primitive type or string that can be retrieved correctly later
|
||||||
* @param readOnly If true, changing the value will have no effect
|
* @param readOnly If true, changing the value will have no effect
|
||||||
* @param T The type of the config value. May be nullable if the getter cannot always return a value
|
* @param T The type of the config value. May be nullable if the getter cannot always return a value
|
||||||
*/
|
*/
|
||||||
class ConfigData<T : Any?> internal constructor(
|
class ConfigData<T : Any?> internal constructor(
|
||||||
var config: IHaveConfig?,
|
val config: IHaveConfig,
|
||||||
override val path: String,
|
override val path: String,
|
||||||
private val primitiveDef: Any,
|
private val primitiveDef: Any,
|
||||||
private val getter: Function<Any, T>,
|
private val getter: Function<Any, T>,
|
||||||
|
@ -47,7 +47,7 @@ class ConfigData<T : Any?> internal constructor(
|
||||||
override fun get(): T {
|
override fun get(): T {
|
||||||
val cachedValue = value
|
val cachedValue = value
|
||||||
if (cachedValue != null) return cachedValue //Speed things up
|
if (cachedValue != null) return cachedValue //Speed things up
|
||||||
val config = config?.config
|
val config = config.config
|
||||||
val freshValue = config?.get(path) ?: primitiveDef.also { setInternal(it) }
|
val freshValue = config?.get(path) ?: primitiveDef.also { setInternal(it) }
|
||||||
return getter.apply(convertPrimitiveType(freshValue)).also { value = it }
|
return getter.apply(convertPrimitiveType(freshValue)).also { value = it }
|
||||||
}
|
}
|
||||||
|
@ -76,9 +76,13 @@ class ConfigData<T : Any?> internal constructor(
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun setInternal(`val`: Any?) {
|
private fun setInternal(`val`: Any?) {
|
||||||
val conf = config ?: return
|
val config = config.config
|
||||||
conf.config.set(path, `val`)
|
if (config != null) {
|
||||||
signalChange(conf)
|
config.set(path, `val`)
|
||||||
|
signalChange(this.config)
|
||||||
|
} else {
|
||||||
|
ChromaUtils.logWarn("Attempted to get/set config value with no config! Path: $path, value: $`val`")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -95,13 +99,9 @@ class ConfigData<T : Any?> internal constructor(
|
||||||
fun signalChange(config: IHaveConfig) {
|
fun signalChange(config: IHaveConfig) {
|
||||||
val cc = config.config
|
val cc = config.config
|
||||||
val sa = config.saveAction
|
val sa = config.saveAction
|
||||||
val root = cc.root
|
val root = cc?.root
|
||||||
if (root == null) {
|
if (root == null) {
|
||||||
if (MainPlugin.isInitialized) {
|
ChromaUtils.logWarn("Attempted to save config with no root! Name: ${cc?.name ?: "NONEXISTENT CONFIG"}")
|
||||||
MainPlugin.instance.logger.warning("Attempted to save config with no root! Name: ${config.config.name}")
|
|
||||||
} else {
|
|
||||||
println("Attempted to save config with no root! Name: ${config.config.name}")
|
|
||||||
}
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!MainPlugin.isInitialized) {
|
if (!MainPlugin.isInitialized) {
|
||||||
|
|
|
@ -15,12 +15,20 @@ class IHaveConfig(
|
||||||
/**
|
/**
|
||||||
* The way the underlying configuration gets saved to disk
|
* The way the underlying configuration gets saved to disk
|
||||||
*/
|
*/
|
||||||
val saveAction: Runnable,
|
saveAction: Runnable,
|
||||||
|
config: ConfigurationSection?
|
||||||
|
) {
|
||||||
|
/**
|
||||||
|
* The way the underlying configuration gets saved to disk
|
||||||
|
*/
|
||||||
|
var saveAction = saveAction
|
||||||
|
private set
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the Bukkit ConfigurationSection. Use [.signalChange] after changing it.
|
* Returns the Bukkit ConfigurationSection. Use [.signalChange] after changing it.
|
||||||
*/
|
*/
|
||||||
var config: ConfigurationSection
|
var config = config
|
||||||
) {
|
private set
|
||||||
private val datamap = HashMap<String, IConfigData<*>>()
|
private val datamap = HashMap<String, IConfigData<*>>()
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -110,8 +118,9 @@ class IHaveConfig(
|
||||||
ConfigData.signalChange(this)
|
ConfigData.signalChange(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun reload(section: ConfigurationSection) {
|
fun reload(section: ConfigurationSection, saveAction: Runnable = this.saveAction) {
|
||||||
config = section
|
config = section
|
||||||
|
this.saveAction = saveAction
|
||||||
datamap.forEach { it.value.reload() }
|
datamap.forEach { it.value.reload() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@ import java.util.function.Predicate
|
||||||
import java.util.function.UnaryOperator
|
import java.util.function.UnaryOperator
|
||||||
|
|
||||||
class ListConfigData<T> internal constructor(
|
class ListConfigData<T> internal constructor(
|
||||||
config: IHaveConfig?,
|
config: IHaveConfig,
|
||||||
path: String,
|
path: String,
|
||||||
primitiveDef: ArrayList<*>,
|
primitiveDef: ArrayList<*>,
|
||||||
private val elementGetter: Function<Any?, T>,
|
private val elementGetter: Function<Any?, T>,
|
||||||
|
@ -27,10 +27,8 @@ class ListConfigData<T> internal constructor(
|
||||||
override val size: Int get() = primitiveList.size
|
override val size: Int get() = primitiveList.size
|
||||||
private fun update() {
|
private fun update() {
|
||||||
val config = listConfig.config
|
val config = listConfig.config
|
||||||
if (config != null) {
|
|
||||||
ConfigData.signalChange(config) //Update the config model and start save task if needed
|
ConfigData.signalChange(config) //Update the config model and start save task if needed
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override fun set(index: Int, element: T): T {
|
override fun set(index: Int, element: T): T {
|
||||||
val ret = primitiveList.set(index, elementSetter.apply(element))
|
val ret = primitiveList.set(index, elementSetter.apply(element))
|
||||||
|
|
Loading…
Reference in a new issue