✨ Use JDBC-based preferences feature/add-h2-database
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Sun, 6 Apr 2025 10:41:35 +0000 (12:41 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Thu, 24 Apr 2025 20:04:41 +0000 (22:04 +0200)
38 files changed:
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/core/Options.java [deleted file]
src/main/java/net/pterodactylus/sone/utils/Option.java [deleted file]
src/main/kotlin/net/pterodactylus/sone/core/Preferences.kt
src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt [deleted file]
src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt [deleted file]
src/main/kotlin/net/pterodactylus/sone/web/ajax/JsonPage.kt
src/main/kotlin/net/pterodactylus/sone/web/pages/BookmarksPage.kt
src/main/kotlin/net/pterodactylus/sone/web/pages/CreateSonePage.kt
src/main/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPage.kt
src/main/kotlin/net/pterodactylus/sone/web/pages/IndexPage.kt
src/main/kotlin/net/pterodactylus/sone/web/pages/LoginPage.kt
src/main/kotlin/net/pterodactylus/sone/web/pages/LogoutPage.kt
src/main/kotlin/net/pterodactylus/sone/web/pages/NewPage.kt
src/main/kotlin/net/pterodactylus/sone/web/pages/OptionsPage.kt
src/main/kotlin/net/pterodactylus/sone/web/pages/SearchPage.kt
src/main/kotlin/net/pterodactylus/sone/web/pages/SoneTemplatePage.kt
src/main/kotlin/net/pterodactylus/sone/web/pages/ViewSonePage.kt
src/test/java/net/pterodactylus/sone/core/OptionsTest.java [deleted file]
src/test/kotlin/net/pterodactylus/sone/core/CoreTest.kt
src/test/kotlin/net/pterodactylus/sone/core/PreferencesLoaderTest.kt [deleted file]
src/test/kotlin/net/pterodactylus/sone/core/PreferencesTest.kt
src/test/kotlin/net/pterodactylus/sone/main/SoneModuleTest.kt
src/test/kotlin/net/pterodactylus/sone/utils/DefaultOptionTest.kt [deleted file]
src/test/kotlin/net/pterodactylus/sone/web/ajax/JsonPageBaseTest.kt
src/test/kotlin/net/pterodactylus/sone/web/ajax/TestObjects.kt
src/test/kotlin/net/pterodactylus/sone/web/pages/BookmarksPageTest.kt
src/test/kotlin/net/pterodactylus/sone/web/pages/CreateSonePageTest.kt
src/test/kotlin/net/pterodactylus/sone/web/pages/ImageBrowserPageTest.kt
src/test/kotlin/net/pterodactylus/sone/web/pages/IndexPageTest.kt
src/test/kotlin/net/pterodactylus/sone/web/pages/LoginPageTest.kt
src/test/kotlin/net/pterodactylus/sone/web/pages/LogoutPageTest.kt
src/test/kotlin/net/pterodactylus/sone/web/pages/NewPageTest.kt
src/test/kotlin/net/pterodactylus/sone/web/pages/OptionsPageTest.kt
src/test/kotlin/net/pterodactylus/sone/web/pages/SearchPageTest.kt
src/test/kotlin/net/pterodactylus/sone/web/pages/SoneTemplatePageTest.kt
src/test/kotlin/net/pterodactylus/sone/web/pages/ViewSonePageTest.kt
src/test/kotlin/net/pterodactylus/sone/web/pages/WebPageTest.kt

index d7cc601..25f1df0 100644 (file)
@@ -115,7 +115,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
        private final AtomicBoolean debug = new AtomicBoolean(false);
 
        /** The preferences. */
-       private final DefaultPreferences preferences;
+       private final Preferences preferences;
 
        /** The event bus. */
        private final EventBus eventBus;
@@ -183,7 +183,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
        private final SoneUriCreator soneUriCreator;
 
        @Inject
-       public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, SoneDownloader soneDownloader, ImageInserter imageInserter, UpdateChecker updateChecker, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database, MetricRegistry metricRegistry, SoneUriCreator soneUriCreator) {
+       public Core(Configuration configuration, FreenetInterface freenetInterface, IdentityManager identityManager, SoneDownloader soneDownloader, ImageInserter imageInserter, UpdateChecker updateChecker, WebOfTrustUpdater webOfTrustUpdater, EventBus eventBus, Database database, MetricRegistry metricRegistry, SoneUriCreator soneUriCreator, Preferences preferences) {
                super("Sone Core");
                this.configuration = configuration;
                this.freenetInterface = freenetInterface;
@@ -196,7 +196,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                this.database = database;
                this.metricRegistry = metricRegistry;
                this.soneUriCreator = soneUriCreator;
-               preferences = new DefaultPreferences(eventBus);
+               this.preferences = preferences;
                this.configurationSaveTimeHistogram = metricRegistry.histogram("configuration.save.duration", () -> new Histogram(new ExponentiallyDecayingReservoir(3000, 0)));
        }
 
@@ -228,7 +228,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         *
         * @return The options of the core
         */
-       public DefaultPreferences getPreferences() {
+       public Preferences getPreferences() {
                return preferences;
        }
 
@@ -1453,10 +1453,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                        storingConfiguration = true;
                }
 
-               /* store the options first. */
                try {
-                       preferences.saveTo(configuration);
-
                        /* save known Sones. */
                        int soneCounter = 0;
                        synchronized (knownSones) {
@@ -1489,8 +1486,6 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
         * Loads the configuration.
         */
        private void loadConfiguration() {
-               new PreferencesLoader(preferences).loadFrom(configuration);
-
                /* load known Sones. */
                int soneCounter = 0;
                while (true) {
diff --git a/src/main/java/net/pterodactylus/sone/core/Options.java b/src/main/java/net/pterodactylus/sone/core/Options.java
deleted file mode 100644 (file)
index 398374a..0000000
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * Sone - Options.java - Copyright © 2010–2020 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program.  If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.core;
-
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.Map;
-
-import net.pterodactylus.sone.utils.Option;
-
-/**
- * Stores various options that influence Sone’s behaviour.
- */
-public class Options {
-
-       /** Holds all {@link Boolean} {@link Option}s. */
-       private final Map<String, Option<Boolean>> booleanOptions = Collections.synchronizedMap(new HashMap<String, Option<Boolean>>());
-
-       /** Holds all {@link Integer} {@link Option}s. */
-       private final Map<String, Option<Integer>> integerOptions = Collections.synchronizedMap(new HashMap<String, Option<Integer>>());
-
-       /** Holds all {@link String} {@link Option}s. */
-       private final Map<String, Option<String>> stringOptions = Collections.synchronizedMap(new HashMap<String, Option<String>>());
-
-       /** Holds all {@link Enum} {@link Option}s. */
-       private final Map<String, Option<? extends Enum<?>>> enumOptions = Collections.synchronizedMap(new HashMap<String, Option<? extends Enum<?>>>());
-
-       /**
-        * Adds a boolean option.
-        *
-        * @param name
-        *            The name of the option
-        * @param booleanOption
-        *            The option
-        * @return The given option
-        */
-       public Option<Boolean> addBooleanOption(String name, Option<Boolean> booleanOption) {
-               booleanOptions.put(name, booleanOption);
-               return booleanOption;
-       }
-
-       /**
-        * Returns the boolean option with the given name.
-        *
-        * @param name
-        *            The name of the option
-        * @return The option, or {@code null} if there is no option with the given
-        *         name
-        */
-       public Option<Boolean> getBooleanOption(String name) {
-               return booleanOptions.get(name);
-       }
-
-       /**
-        * Adds an {@link Integer} {@link Option}.
-        *
-        * @param name
-        *            The name of the option
-        * @param integerOption
-        *            The option
-        * @return The given option
-        */
-       public Option<Integer> addIntegerOption(String name, Option<Integer> integerOption) {
-               integerOptions.put(name, integerOption);
-               return integerOption;
-       }
-
-       /**
-        * Returns an {@link Integer} {@link Option}.
-        *
-        * @param name
-        *            The name of the integer option to get
-        * @return The integer option, or {@code null} if there is no option with
-        *         the given name
-        */
-       public Option<Integer> getIntegerOption(String name) {
-               return integerOptions.get(name);
-       }
-
-       /**
-        * Adds a {@link String} {@link Option}.
-        *
-        * @param name
-        *            The name of the option
-        * @param stringOption
-        *            The option
-        * @return The given option
-        */
-       public Option<String> addStringOption(String name, Option<String> stringOption) {
-               stringOptions.put(name, stringOption);
-               return stringOption;
-       }
-
-       /**
-        * Returns a {@link String} {@link Option}.
-        *
-        * @param name
-        *            The name of the string option to get
-        * @return The string option, or {@code null} if there is no option with the
-        *         given name
-        */
-       public Option<String> getStringOption(String name) {
-               return stringOptions.get(name);
-       }
-
-       /**
-        * Adds an {@link Enum} {@link Option}.
-        *
-        * @param <T>
-        *            The enum type
-        * @param name
-        *            The name of the option
-        * @param enumOption
-        *            The option
-        * @return The given option
-        */
-       public <T extends Enum<T>> Option<T> addEnumOption(String name, Option<T> enumOption) {
-               enumOptions.put(name, enumOption);
-               return enumOption;
-       }
-
-       /**
-        * Returns a {@link Enum} {@link Option}. As the type can probably not be
-        * interred correctly you could help the compiler by calling this method
-        * like this:
-        * <p>
-        *
-        * <pre>
-        * options.&lt;SomeEnum&gt; getEnumOption(&quot;SomeEnumOption&quot;).get();
-        * </pre>
-        *
-        * @param <T>
-        *            The enum type
-        * @param name
-        *            The name of the option
-        * @return The enum option, or {@code null} if there is no enum option with
-        *         the given name
-        */
-       @SuppressWarnings("unchecked")
-       public <T extends Enum<T>> Option<T> getEnumOption(String name) {
-               return (Option<T>) enumOptions.get(name);
-       }
-
-}
diff --git a/src/main/java/net/pterodactylus/sone/utils/Option.java b/src/main/java/net/pterodactylus/sone/utils/Option.java
deleted file mode 100644 (file)
index eca96f5..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-package net.pterodactylus.sone.utils;
-
-/**
- * Contains current and default value of an option.
- *
- * @param <T>
- *            The type of the option
- */
-public interface Option<T> {
-
-       /**
-        * Returns the current value of the option. If the current value is not
-        * set (usually {@code null}), the default value is returned.
-        *
-        * @return The current value of the option
-        */
-       public T get();
-
-       /**
-        * Returns the real value of the option. This will also return an unset
-        * value (usually {@code null})!
-        *
-        * @return The real value of the option
-        */
-       public T getReal();
-
-       /**
-        * Validates the given value. Note that {@code null} is always a valid
-        * value!
-        *
-        * @param value
-        *            The value to validate
-        * @return {@code true} if this option does not have a validator, or the
-        *         validator validates this object, {@code false} otherwise
-        */
-       public boolean validate(T value);
-
-       /**
-        * Sets the current value of the option.
-        *
-        * @param value
-        *            The new value of the option
-        * @throws IllegalArgumentException
-        *             if the value is not valid for this option
-        */
-       public void set(T value) throws IllegalArgumentException;
-
-}
index b701cc3..bcbb231 100644 (file)
@@ -29,110 +29,12 @@ import net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.ALWAYS
 import net.pterodactylus.sone.fcp.event.FcpInterfaceActivatedEvent
 import net.pterodactylus.sone.fcp.event.FcpInterfaceDeactivatedEvent
 import net.pterodactylus.sone.fcp.event.FullAccessRequiredChanged
-import net.pterodactylus.sone.utils.DefaultOption
-import net.pterodactylus.util.config.Configuration
-import net.pterodactylus.util.config.ConfigurationException
-import java.lang.Integer.MAX_VALUE
 
 /**
- * Convenience interface for external classes that want to access the core’s
- * configuration.
+ * Sone’s user-independent preferences. Clients of this interface can assume
+ * that none of the properties will be {@code null}, especially when the
+ * instance has been injected via Guice.
  */
-class DefaultPreferences(private val eventBus: EventBus) {
-
-       private val _insertionDelay = DefaultOption(60) { it in 0..MAX_VALUE }
-       val insertionDelay: Int get() = _insertionDelay.get()
-       var newInsertionDelay: Int?
-               get() = unsupported
-               set(value) {
-                       _insertionDelay.set(value)
-                       eventBus.post(InsertionDelayChangedEvent(insertionDelay))
-               }
-
-       private val _postsPerPage = DefaultOption(10) { it in 1..MAX_VALUE }
-       val postsPerPage: Int get() = _postsPerPage.get()
-       var newPostsPerPage: Int?
-               get() = unsupported
-               set(value) {
-                       _postsPerPage.set(value)
-               }
-
-       private val _imagesPerPage = DefaultOption(9) { it in 1..MAX_VALUE }
-       val imagesPerPage: Int get() = _imagesPerPage.get()
-       var newImagesPerPage: Int?
-               get() = unsupported
-               set(value: Int?) = _imagesPerPage.set(value)
-
-       private val _charactersPerPost = DefaultOption(400) { it == -1 || it in 50..MAX_VALUE }
-       val charactersPerPost: Int get() = _charactersPerPost.get()
-       var newCharactersPerPost: Int?
-               get() = unsupported
-               set(value) = _charactersPerPost.set(value)
-
-       private val _postCutOffLength = DefaultOption(200) { it in 50..MAX_VALUE }
-       val postCutOffLength: Int get() = _postCutOffLength.get()
-       var newPostCutOffLength: Int?
-               get() = unsupported
-               set(value) = _postCutOffLength.set(value)
-
-       private val _requireFullAccess = DefaultOption(false)
-       val requireFullAccess: Boolean get() = _requireFullAccess.get()
-       var newRequireFullAccess: Boolean?
-               get() = unsupported
-               set(value) = _requireFullAccess.set(value)
-
-       private val _fcpInterfaceActive = DefaultOption(false)
-       val fcpInterfaceActive: Boolean get() = _fcpInterfaceActive.get()
-       var newFcpInterfaceActive: Boolean?
-               get() = unsupported
-               set(value) {
-                       _fcpInterfaceActive.set(value)
-                       when (value) {
-                               true -> eventBus.post(FcpInterfaceActivatedEvent())
-                               else -> eventBus.post(FcpInterfaceDeactivatedEvent())
-                       }
-               }
-
-       private val _fcpFullAccessRequired = DefaultOption(ALWAYS)
-       val fcpFullAccessRequired: FullAccessRequired get() = _fcpFullAccessRequired.get()
-       var newFcpFullAccessRequired: FullAccessRequired?
-               get() = unsupported
-               set(value) {
-                       _fcpFullAccessRequired.set(value)
-                       eventBus.post(FullAccessRequiredChanged(fcpFullAccessRequired))
-               }
-
-       private val _strictFiltering = DefaultOption(false)
-       val strictFiltering: Boolean get() = _strictFiltering.get()
-       var newStrictFiltering: Boolean? = false
-               set(value) {
-                       _strictFiltering.set(value)
-                       when (strictFiltering) {
-                               true -> eventBus.post(StrictFilteringActivatedEvent())
-                               else -> eventBus.post(StrictFilteringDeactivatedEvent())
-                       }
-               }
-
-       @Throws(ConfigurationException::class)
-       fun saveTo(configuration: Configuration) {
-               configuration.getIntValue("Option/ConfigurationVersion").value = 0
-               configuration.getIntValue("Option/InsertionDelay").value = _insertionDelay.real
-               configuration.getIntValue("Option/PostsPerPage").value = _postsPerPage.real
-               configuration.getIntValue("Option/ImagesPerPage").value = _imagesPerPage.real
-               configuration.getIntValue("Option/CharactersPerPost").value = _charactersPerPost.real
-               configuration.getIntValue("Option/PostCutOffLength").value = _postCutOffLength.real
-               configuration.getBooleanValue("Option/RequireFullAccess").value = _requireFullAccess.real
-               configuration.getBooleanValue("Option/ActivateFcpInterface").value = _fcpInterfaceActive.real
-               configuration.getIntValue("Option/FcpFullAccessRequired").value = toInt(_fcpFullAccessRequired.real)
-               configuration.getBooleanValue("Option/StrictFiltering").value = _strictFiltering.real
-       }
-
-       private fun toInt(fullAccessRequired: FullAccessRequired?): Int? {
-               return fullAccessRequired?.ordinal
-       }
-
-}
-
 interface Preferences {
 
        var insertionDelay: Int?
@@ -147,8 +49,6 @@ interface Preferences {
 
 }
 
-private val unsupported: Nothing get() = throw UnsupportedOperationException()
-
 fun Preferences.withDefaults() = object : Preferences by this {
        override var insertionDelay: Int? by default(60, this@withDefaults::insertionDelay)
        override var postsPerPage: Int? by default(10, this@withDefaults::postsPerPage)
diff --git a/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt b/src/main/kotlin/net/pterodactylus/sone/core/PreferencesLoader.kt
deleted file mode 100644 (file)
index 5e8b7e8..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-package net.pterodactylus.sone.core
-
-import net.pterodactylus.sone.fcp.FcpInterface.*
-import net.pterodactylus.util.config.*
-
-/**
- * Loads preferences stored in a [Configuration] into a [DefaultPreferences] object.
- */
-class PreferencesLoader(private val preferences: DefaultPreferences) {
-
-       fun loadFrom(configuration: Configuration) {
-               loadInsertionDelay(configuration)
-               loadPostsPerPage(configuration)
-               loadImagesPerPage(configuration)
-               loadCharactersPerPost(configuration)
-               loadPostCutOffLength(configuration)
-               loadRequireFullAccess(configuration)
-               loadFcpInterfaceActive(configuration)
-               loadFcpFullAccessRequired(configuration)
-               loadStrictFiltering(configuration)
-       }
-
-       private fun loadInsertionDelay(configuration: Configuration) {
-               preferences.newInsertionDelay = configuration.getIntValue("Option/InsertionDelay").getValue(null)
-       }
-
-       private fun loadPostsPerPage(configuration: Configuration) {
-               preferences.newPostsPerPage = configuration.getIntValue("Option/PostsPerPage").getValue(null)
-       }
-
-       private fun loadImagesPerPage(configuration: Configuration) {
-               preferences.newImagesPerPage = configuration.getIntValue("Option/ImagesPerPage").getValue(null)
-       }
-
-       private fun loadCharactersPerPost(configuration: Configuration) {
-               preferences.newCharactersPerPost = configuration.getIntValue("Option/CharactersPerPost").getValue(null)
-       }
-
-       private fun loadPostCutOffLength(configuration: Configuration) {
-               try {
-                       preferences.newPostCutOffLength = configuration.getIntValue("Option/PostCutOffLength").getValue(null)
-               } catch (iae1: IllegalArgumentException) { /* previous versions allowed -1, ignore and use default. */
-               }
-       }
-
-       private fun loadRequireFullAccess(configuration: Configuration) {
-               preferences.newRequireFullAccess = configuration.getBooleanValue("Option/RequireFullAccess").getValue(null)
-       }
-
-       private fun loadFcpInterfaceActive(configuration: Configuration) {
-               preferences.newFcpInterfaceActive = configuration.getBooleanValue("Option/ActivateFcpInterface").getValue(null)
-       }
-
-       private fun loadFcpFullAccessRequired(configuration: Configuration) {
-               val fullAccessRequiredInteger = configuration.getIntValue("Option/FcpFullAccessRequired").getValue(null)
-               preferences.newFcpFullAccessRequired = fullAccessRequiredInteger?.let { FullAccessRequired.values()[it] }
-       }
-
-       private fun loadStrictFiltering(configuration: Configuration) {
-               preferences.newStrictFiltering = configuration.getBooleanValue("Option/StrictFiltering").getValue(null)
-       }
-
-}
diff --git a/src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt b/src/main/kotlin/net/pterodactylus/sone/utils/DefaultOption.kt
deleted file mode 100644 (file)
index fd4215f..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-package net.pterodactylus.sone.utils
-
-/**
- * Basic implementation of an [Option].
- *
- * @param <T> The type of the option
- */
-class DefaultOption<T> @JvmOverloads constructor(
-               private val defaultValue: T,
-               private val validator: ((T) -> Boolean)? = null
-) : Option<T> {
-
-       @Volatile
-       private var value: T? = null
-
-       override fun get() = value ?: defaultValue
-
-       override fun getReal(): T? = value
-
-       override fun validate(value: T?): Boolean =
-                       value == null || validator?.invoke(value) ?: true
-
-       override fun set(value: T?) {
-               require(validate(value)) { "New Value ($value) could not be validated." }
-               this.value = value
-       }
-
-}
index 356aca6..af5e4e6 100644 (file)
@@ -35,7 +35,7 @@ abstract class JsonPage(protected val webInterface: WebInterface) : Page<Freenet
                        sessionProvider.getCurrentSone(toadletContext)
 
        override fun handleRequest(request: FreenetRequest, response: Response): Response {
-               if (core.preferences.requireFullAccess && !request.toadletContext.isAllowedFullAccess) {
+               if (core.preferences.requireFullAccess!! && !request.toadletContext.isAllowedFullAccess) {
                        return response.setStatusCode(403).setStatusText("Forbidden").setContentType("application/json").write(createErrorJsonObject("auth-required").asJsonString())
                }
                if (needsFormPassword && request.parameters["formPassword"] != webInterface.formPassword) {
index 5e67242..00a4484 100644 (file)
@@ -19,7 +19,7 @@ class BookmarksPage @Inject constructor(webInterface: WebInterface, loaders: Loa
 
        override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
                soneRequest.core.bookmarkedPosts.let { posts ->
-                       val pagination = posts.filter(Post::isLoaded).sortedByDescending { it.time }.paginate(soneRequest.core.preferences.postsPerPage)
+                       val pagination = posts.filter(Post::isLoaded).sortedByDescending { it.time }.paginate(soneRequest.core.preferences.postsPerPage!!)
                        templateContext["pagination"] = pagination
                        templateContext["posts"] = pagination.items
                        templateContext["postsNotLoaded"] = posts.any { !it.isLoaded }
index a42150b..3e8d5a5 100644 (file)
@@ -39,7 +39,7 @@ class CreateSonePage @Inject constructor(webInterface: WebInterface, loaders: Lo
        }
 
        override fun isEnabled(soneRequest: SoneRequest) =
-                       if (soneRequest.core.preferences.requireFullAccess && !soneRequest.toadletContext.isAllowedFullAccess) {
+                       if (soneRequest.core.preferences.requireFullAccess!! && !soneRequest.toadletContext.isAllowedFullAccess) {
                                false
                        } else {
                                (getCurrentSone(soneRequest.toadletContext) == null) || (soneRequest.core.localSones.size == 1)
index cd4d0f2..8433232 100644 (file)
@@ -33,7 +33,7 @@ class ImageBrowserPage @Inject constructor(webInterface: WebInterface, loaders:
                                        .filterNot(Album::isEmpty)
                                        .sortedBy(Album::getTitle)
                                        .also { albums ->
-                                               albums.paginate(soneRequest.core.preferences.imagesPerPage)
+                                               albums.paginate(soneRequest.core.preferences.imagesPerPage!!)
                                                                .turnTo(soneRequest.parameters["page"]?.toIntOrNull() ?: 0)
                                                                .also { pagination ->
                                                                        templateContext["albumPagination"] = pagination
index 6a64b16..7138400 100644 (file)
@@ -29,7 +29,7 @@ class IndexPage @Inject constructor(webInterface: WebInterface, loaders: Loaders
                                .filter { postVisibilityFilter.isVisible(currentSone).invoke(it) }
                                .sortedByDescending { it.time }
                                .let { posts ->
-                                       posts.paginate(soneRequest.core.preferences.postsPerPage)
+                                       posts.paginate(soneRequest.core.preferences.postsPerPage!!)
                                                        .turnTo(soneRequest.parameters["page"]?.toIntOrNull() ?: 0)
                                                        .let { pagination ->
                                                                templateContext["pagination"] = pagination
index 952d8e7..f77a201 100644 (file)
@@ -34,7 +34,7 @@ class LoginPage @Inject constructor(webInterface: WebInterface, loaders: Loaders
                        getCurrentSone(request.toadletContext)?.let { "index.html" }
 
        override fun isEnabled(soneRequest: SoneRequest) = when {
-               soneRequest.core.preferences.requireFullAccess && !soneRequest.toadletContext.isAllowedFullAccess -> false
+               soneRequest.core.preferences.requireFullAccess!! && !soneRequest.toadletContext.isAllowedFullAccess -> false
                else -> getCurrentSone(soneRequest.toadletContext) == null
        }
 
index b65a7ed..803e91a 100644 (file)
@@ -21,7 +21,7 @@ class LogoutPage @Inject constructor(webInterface: WebInterface, loaders: Loader
        }
 
        override fun isEnabled(soneRequest: SoneRequest): Boolean =
-                       if (soneRequest.core.preferences.requireFullAccess && !soneRequest.toadletContext.isAllowedFullAccess) {
+                       if (soneRequest.core.preferences.requireFullAccess!! && !soneRequest.toadletContext.isAllowedFullAccess) {
                                false
                        } else
                                getCurrentSone(soneRequest.toadletContext) != null && soneRequest.core.localSones.size != 1
index 9724c56..d76663b 100644 (file)
@@ -30,7 +30,7 @@ class NewPage @Inject constructor(webInterface: WebInterface, loaders: Loaders,
                                                .distinct()
                                                .sortedByDescending { it.time }
                                                .let { posts ->
-                                                       posts.paginate(soneRequest.core.preferences.postsPerPage)
+                                                       posts.paginate(soneRequest.core.preferences.postsPerPage!!)
                                                                        .turnTo(soneRequest.parameters["page"]?.toIntOrNull() ?: 0)
                                                                        .let { pagination ->
                                                                                templateContext["pagination"] = pagination
index 6362cdb..2f9d548 100644 (file)
@@ -42,9 +42,9 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade
                        val fcpInterfaceActive = "fcp-interface-active" in soneRequest.parameters
                        val strictFiltering = "strict-filtering" in soneRequest.parameters
 
-                       soneRequest.core.preferences.newRequireFullAccess = fullAccessRequired
-                       soneRequest.core.preferences.newFcpInterfaceActive = fcpInterfaceActive
-                       soneRequest.core.preferences.newStrictFiltering = strictFiltering
+                       soneRequest.core.preferences.requireFullAccess = fullAccessRequired
+                       soneRequest.core.preferences.fcpInterfaceActive = fcpInterfaceActive
+                       soneRequest.core.preferences.strictFiltering = strictFiltering
 
                        val postsPerPage = soneRequest.parameters["posts-per-page"]?.toIntOrNull()
                        val charactersPerPost = soneRequest.parameters["characters-per-post"]?.toIntOrNull()
@@ -53,12 +53,12 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade
                        val insertionDelay = soneRequest.parameters["insertion-delay"]?.toIntOrNull()
                        val fcpFullAccessRequired = soneRequest.parameters["fcp-full-access-required"]?.toIntOrNull()
 
-                       if (cantSetOption { soneRequest.core.preferences.newPostsPerPage = postsPerPage }) fieldsWithErrors += "posts-per-page"
-                       if (cantSetOption { soneRequest.core.preferences.newCharactersPerPost = charactersPerPost }) fieldsWithErrors += "characters-per-post"
-                       if (cantSetOption { soneRequest.core.preferences.newPostCutOffLength = postCutOffLength }) fieldsWithErrors += "post-cut-off-length"
-                       if (cantSetOption { soneRequest.core.preferences.newImagesPerPage = imagesPerPage }) fieldsWithErrors += "images-per-page"
-                       if (cantSetOption { soneRequest.core.preferences.newInsertionDelay = insertionDelay }) fieldsWithErrors += "insertion-delay"
-                       fcpFullAccessRequired?.also { if (cantSetOption { soneRequest.core.preferences.newFcpFullAccessRequired = FullAccessRequired.values()[fcpFullAccessRequired] }) fieldsWithErrors += "fcp-full-access-required" }
+                       if (cantSetOption { soneRequest.core.preferences.postsPerPage = postsPerPage }) fieldsWithErrors += "posts-per-page"
+                       if (cantSetOption { soneRequest.core.preferences.charactersPerPost = charactersPerPost }) fieldsWithErrors += "characters-per-post"
+                       if (cantSetOption { soneRequest.core.preferences.postCutOffLength = postCutOffLength }) fieldsWithErrors += "post-cut-off-length"
+                       if (cantSetOption { soneRequest.core.preferences.imagesPerPage = imagesPerPage }) fieldsWithErrors += "images-per-page"
+                       if (cantSetOption { soneRequest.core.preferences.insertionDelay = insertionDelay }) fieldsWithErrors += "insertion-delay"
+                       fcpFullAccessRequired?.also { if (cantSetOption { soneRequest.core.preferences.fcpFullAccessRequired = FullAccessRequired.values()[fcpFullAccessRequired] }) fieldsWithErrors += "fcp-full-access-required" }
 
                        if (fieldsWithErrors.isEmpty()) {
                                soneRequest.core.touchConfiguration()
@@ -78,7 +78,7 @@ class OptionsPage @Inject constructor(webInterface: WebInterface, loaders: Loade
                soneRequest.core.preferences.let { preferences ->
                        templateContext["insertion-delay"] = preferences.insertionDelay
                        templateContext["characters-per-post"] = preferences.charactersPerPost
-                       templateContext["fcp-full-access-required"] = preferences.fcpFullAccessRequired.ordinal
+                       templateContext["fcp-full-access-required"] = preferences.fcpFullAccessRequired!!.ordinal
                        templateContext["images-per-page"] = preferences.imagesPerPage
                        templateContext["fcp-interface-active"] = preferences.fcpInterfaceActive
                        templateContext["require-full-access"] = preferences.requireFullAccess
index a546ce9..c642ef9 100644 (file)
@@ -54,13 +54,13 @@ class SearchPage(webInterface: WebInterface, loaders: Loaders, templateRenderer:
 
                val soneNameCache = { sone: Sone -> sone.names() }.memoize()
                val sonePagination = soneRequest.core.sones
-                               .scoreAndPaginate(phrases, soneRequest.core.preferences.postsPerPage) { it.allText(soneNameCache) }
+                               .scoreAndPaginate(phrases, soneRequest.core.preferences.postsPerPage!!) { it.allText(soneNameCache) }
                                .apply { page = soneRequest.parameters["sonePage"].emptyToNull?.toIntOrNull() ?: 0 }
                val postPagination = cache.get(phrases) {
                        soneRequest.core.sones
                                        .flatMap(Sone::getPosts)
                                        .filter(noFuturePost)
-                                       .scoreAndPaginate(phrases, soneRequest.core.preferences.postsPerPage) { it.allText(soneNameCache, soneRequest.core::getReplies) }
+                                       .scoreAndPaginate(phrases, soneRequest.core.preferences.postsPerPage!!) { it.allText(soneNameCache, soneRequest.core::getReplies) }
                }.apply { page = soneRequest.parameters["postPage"].emptyToNull?.toIntOrNull() ?: 0 }
 
                Logger.normal(SearchPage::class.java, "Finished search for “${soneRequest.parameters["query"]}” in ${System.currentTimeMillis() - startTime}ms.")
index 7b81c50..34107a1 100644 (file)
@@ -95,7 +95,7 @@ open class SoneTemplatePage(
 
        open fun isEnabled(soneRequest: SoneRequest) = when {
                requiresLogin && getCurrentSone(soneRequest.toadletContext) == null -> false
-               core.preferences.requireFullAccess && !soneRequest.toadletContext.isAllowedFullAccess -> false
+               core.preferences.requireFullAccess!! && !soneRequest.toadletContext.isAllowedFullAccess -> false
                else -> true
        }
 
index 1015633..d78238a 100644 (file)
@@ -26,7 +26,7 @@ class ViewSonePage @Inject constructor(webInterface: WebInterface, loaders: Load
                        val directedPosts = soneRequest.core.getDirectedPosts(sone.id)
                        (sonePosts + directedPosts)
                                        .sortedByDescending(Post::getTime)
-                                       .paginate(soneRequest.core.preferences.postsPerPage)
+                                       .paginate(soneRequest.core.preferences.postsPerPage!!)
                                        .apply { page = soneRequest.parameters["postPage"]?.toIntOrNull() ?: 0 }
                                        .also {
                                                templateContext["postPagination"] = it
@@ -38,7 +38,7 @@ class ViewSonePage @Inject constructor(webInterface: WebInterface, loaders: Load
                                        .minus(sonePosts)
                                        .minus(directedPosts)
                                        .sortedByDescending { soneRequest.core.getReplies(it.id).first().time }
-                                       .paginate(soneRequest.core.preferences.postsPerPage)
+                                       .paginate(soneRequest.core.preferences.postsPerPage!!)
                                        .apply { page = soneRequest.parameters["repliedPostPage"]?.toIntOrNull() ?: 0 }
                                        .also {
                                                templateContext["repliedPostPagination"] = it
diff --git a/src/test/java/net/pterodactylus/sone/core/OptionsTest.java b/src/test/java/net/pterodactylus/sone/core/OptionsTest.java
deleted file mode 100644 (file)
index 113430c..0000000
+++ /dev/null
@@ -1,53 +0,0 @@
-package net.pterodactylus.sone.core;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.nullValue;
-import static org.mockito.Mockito.mock;
-
-import net.pterodactylus.sone.utils.Option;
-
-import org.junit.Test;
-
-/**
- * Unit test for {@link Options}.
- */
-public class OptionsTest {
-
-       private final Options options = new Options();
-
-       @Test
-       public void booleanOptionIsAdded() {
-               Option<Boolean> booleanOption = mock(Option.class);
-               options.addBooleanOption("test", booleanOption);
-               assertThat(options.getBooleanOption("test"), is(booleanOption));
-               assertThat(options.getBooleanOption("not-test"), nullValue());
-       }
-
-       @Test
-       public void integerOptionIsAdded() {
-               Option<Integer> integerOption = mock(Option.class);
-               options.addIntegerOption("test", integerOption);
-               assertThat(options.getIntegerOption("test"), is(integerOption));
-               assertThat(options.getIntegerOption("not-test"), nullValue());
-       }
-
-       @Test
-       public void stringOptionIsAdded() {
-               Option<String> stringOption = mock(Option.class);
-               options.addStringOption("test", stringOption);
-               assertThat(options.getStringOption("test"), is(stringOption));
-               assertThat(options.getStringOption("not-test"), nullValue());
-       }
-
-       @Test
-       public void enumOptionIsAdded() {
-               Option<TestEnum> enumOption = mock(Option.class);
-               options.addEnumOption("test", enumOption);
-               assertThat(options.<TestEnum>getEnumOption("test"), is(enumOption));
-               assertThat(options.<TestEnum>getEnumOption("not-test"), nullValue());
-       }
-
-       private enum TestEnum {TEST, NOT_TEST}
-
-}
index 39273d0..65036f9 100644 (file)
@@ -53,7 +53,7 @@ class CoreTest {
                val database = mock<Database>()
                val metricRegistry = MetricRegistry()
                val soneUriCreator = SoneUriCreator()
-               val core = Core(configuration, freenetInterface, identityManager, soneDownloader, imageInserter, updateChecker, webOfTrustUpdater, eventBus, database, metricRegistry, soneUriCreator)
+               val core = Core(configuration, freenetInterface, identityManager, soneDownloader, imageInserter, updateChecker, webOfTrustUpdater, eventBus, database, metricRegistry, soneUriCreator, testPreferences())
                val ownIdentity = mock<OwnIdentity>()
                val identity = mock<Identity>()
                whenever(identity.id).thenReturn("sone-id")
@@ -171,7 +171,7 @@ class CoreTest {
                val database = mock<Database>()
                val metricRegistry = MetricRegistry()
                val soneUriCreator = SoneUriCreator()
-               return Core(configuration, freenetInterface, identityManager, soneDownloader, imageInserter, updateChecker, webOfTrustUpdater, eventBus, database, metricRegistry, soneUriCreator)
+               return Core(configuration, freenetInterface, identityManager, soneDownloader, imageInserter, updateChecker, webOfTrustUpdater, eventBus, database, metricRegistry, soneUriCreator, testPreferences())
        }
 
 }
diff --git a/src/test/kotlin/net/pterodactylus/sone/core/PreferencesLoaderTest.kt b/src/test/kotlin/net/pterodactylus/sone/core/PreferencesLoaderTest.kt
deleted file mode 100644 (file)
index a90d87c..0000000
+++ /dev/null
@@ -1,63 +0,0 @@
-package net.pterodactylus.sone.core
-
-import com.google.common.eventbus.*
-import net.pterodactylus.sone.fcp.FcpInterface.*
-import net.pterodactylus.util.config.*
-import org.hamcrest.MatcherAssert.*
-import org.hamcrest.Matchers.*
-import org.junit.*
-
-/**
- * Unit test for [PreferencesLoader].
- */
-class PreferencesLoaderTest {
-
-       @Suppress("UnstableApiUsage")
-       private val eventBus = EventBus()
-       private val preferences = DefaultPreferences(eventBus)
-       private val configuration = Configuration(MapConfigurationBackend())
-       private val preferencesLoader = PreferencesLoader(preferences)
-
-       @Before
-       fun setupConfiguration() {
-               setupIntValue("InsertionDelay", 15)
-               setupIntValue("PostsPerPage", 25)
-               setupIntValue("ImagesPerPage", 12)
-               setupIntValue("CharactersPerPost", 150)
-               setupIntValue("PostCutOffLength", 300)
-               setupBooleanValue("RequireFullAccess", true)
-               setupBooleanValue("ActivateFcpInterface", true)
-               setupIntValue("FcpFullAccessRequired", 1)
-               setupBooleanValue("StrictFiltering", true)
-       }
-
-       private fun setupIntValue(optionName: String, value: Int) {
-               configuration.getIntValue("Option/$optionName").value = value
-       }
-
-       private fun setupBooleanValue(optionName: String, value: Boolean) {
-               configuration.getBooleanValue("Option/$optionName").value = value
-       }
-
-       @Test
-       fun `configuration is loaded correctly`() {
-               preferencesLoader.loadFrom(configuration)
-               assertThat(preferences.insertionDelay, equalTo(15))
-               assertThat(preferences.postsPerPage, equalTo(25))
-               assertThat(preferences.imagesPerPage, equalTo(12))
-               assertThat(preferences.charactersPerPost, equalTo(150))
-               assertThat(preferences.postCutOffLength, equalTo(300))
-               assertThat(preferences.requireFullAccess, equalTo(true))
-               assertThat(preferences.fcpInterfaceActive, equalTo(true))
-               assertThat(preferences.fcpFullAccessRequired, equalTo(FullAccessRequired.WRITING))
-               assertThat(preferences.strictFiltering, equalTo(true))
-       }
-
-       @Test
-       fun `configuration is loaded correctly with cut off length minus one`() {
-               setupIntValue("PostCutOffLength", -1)
-               preferencesLoader.loadFrom(configuration)
-               assertThat(preferences.postCutOffLength, not(equalTo(-1)))
-       }
-
-}
index cd4d9cd..f7cbcdf 100644 (file)
@@ -15,344 +15,11 @@ import net.pterodactylus.sone.fcp.event.FcpInterfaceDeactivatedEvent
 import net.pterodactylus.sone.fcp.event.FullAccessRequiredChanged
 import net.pterodactylus.sone.test.allNullPreferences
 import net.pterodactylus.sone.test.assertThrows
-import net.pterodactylus.util.config.Configuration
-import net.pterodactylus.util.config.MapConfigurationBackend
-import org.hamcrest.Matcher
 import org.hamcrest.MatcherAssert.assertThat
 import org.hamcrest.Matchers.contains
-import org.hamcrest.Matchers.emptyIterable
 import org.hamcrest.Matchers.equalTo
-import org.hamcrest.Matchers.hasItem
-import org.hamcrest.Matchers.instanceOf
-import org.hamcrest.Matchers.nullValue
 import org.junit.Test
 
-/**
- * Unit test for [DefaultPreferences].
- */
-class DefaultPreferencesTest {
-
-       private val eventBus = EventBus()
-       private val preferences = DefaultPreferences(eventBus)
-
-       @Test
-       fun `preferences retain insertion delay`() {
-               preferences.newInsertionDelay = 15
-               assertThat(preferences.insertionDelay, equalTo(15))
-       }
-
-       @Test
-       fun `preferences sends event on setting insertion delay`() {
-               val events = mutableListOf<InsertionDelayChangedEvent>()
-               eventBus.register(object {
-                       @Subscribe
-                       fun insertionDelayChangedEvent(event: InsertionDelayChangedEvent) =
-                                       events.add(event)
-               })
-               preferences.newInsertionDelay = 15
-               assertThat(events, hasItem(InsertionDelayChangedEvent(15)))
-       }
-
-       @Test(expected = IllegalArgumentException::class)
-       fun `invalid insertion delay is rejected`() {
-               preferences.newInsertionDelay = -15
-       }
-
-       @Test
-       fun `no event is sent when invalid insertion delay is set`() {
-               val events = mutableListOf<InsertionDelayChangedEvent>()
-               eventBus.register(object {
-                       @Subscribe
-                       fun insertionDelayChanged(event: InsertionDelayChangedEvent) =
-                                       events.add(event)
-               })
-               try {
-                       preferences.newInsertionDelay = -15
-               } catch (iae: IllegalArgumentException) {
-                       /* ignore. */
-               }
-
-               assertThat(events, emptyIterable())
-       }
-
-       @Test
-       fun `preferences return default value when insertion delay is set to null`() {
-               preferences.newInsertionDelay = null
-               assertThat(preferences.insertionDelay, equalTo(60))
-       }
-
-       @Test
-       fun `preferences start with insertion delay default value`() {
-               assertThat(preferences.insertionDelay, equalTo(60))
-       }
-
-       @Test
-       fun `preferences saves null for default insertion delay setting`() {
-               verifySavedOption(nullValue()) { it.getIntValue("Option/InsertionDelay").getValue(null) }
-       }
-
-       @Test
-       fun `preferences retain posts per page`() {
-               preferences.newPostsPerPage = 15
-               assertThat(preferences.postsPerPage, equalTo(15))
-       }
-
-       @Test(expected = IllegalArgumentException::class)
-       fun `invalid posts per page is rejected`() {
-               preferences.newPostsPerPage = -15
-       }
-
-       @Test
-       fun `preferences return default value when posts per page is set to null`() {
-               preferences.newPostsPerPage = null
-               assertThat(preferences.postsPerPage, equalTo(10))
-       }
-
-       @Test
-       fun `preferences start with posts per page default value`() {
-               assertThat(preferences.postsPerPage, equalTo(10))
-       }
-
-       @Test
-       fun `preferences retain images per page`() {
-               preferences.newImagesPerPage = 15
-               assertThat(preferences.imagesPerPage, equalTo(15))
-       }
-
-       @Test(expected = IllegalArgumentException::class)
-       fun `invalid images per page is rejected`() {
-               preferences.newImagesPerPage = -15
-       }
-
-       @Test
-       fun `preferences return default value when images per page is set to null`() {
-               preferences.newImagesPerPage = null
-               assertThat(preferences.imagesPerPage, equalTo(9))
-       }
-
-       @Test
-       fun `preferences start with images per page default value`() {
-               assertThat(preferences.imagesPerPage, equalTo(9))
-       }
-
-       @Test
-       fun `preferences retain characters per post`() {
-               preferences.newCharactersPerPost = 150
-               assertThat(preferences.charactersPerPost, equalTo(150))
-       }
-
-       @Test(expected = IllegalArgumentException::class)
-       fun `invalid characters per post is rejected`() {
-               preferences.newCharactersPerPost = -15
-       }
-
-       @Test
-       fun `preferences return default value when characters per post is set to null`() {
-               preferences.newCharactersPerPost = null
-               assertThat(preferences.charactersPerPost, equalTo(400))
-       }
-
-       @Test
-       fun `preferences start with characters per post default value`() {
-               assertThat(preferences.charactersPerPost, equalTo(400))
-       }
-
-       @Test
-       fun `preferences retain post cut off length`() {
-               preferences.newPostCutOffLength = 150
-               assertThat(preferences.postCutOffLength, equalTo(150))
-       }
-
-       @Test(expected = IllegalArgumentException::class)
-       fun `invalid post cut off length is rejected`() {
-               preferences.newPostCutOffLength = -15
-       }
-
-       @Test(expected = IllegalArgumentException::class)
-       fun `cut off length of minus one is not allowed`() {
-               preferences.newPostCutOffLength = -1
-       }
-
-       @Test
-       fun `preferences return default value when post cut off length is set to null`() {
-               preferences.newPostCutOffLength = null
-               assertThat(preferences.postCutOffLength, equalTo(200))
-       }
-
-       @Test
-       fun `preferences start with post cut off length default value`() {
-               assertThat(preferences.postCutOffLength, equalTo(200))
-       }
-
-       @Test
-       fun `preferences retain require full access of true`() {
-               preferences.newRequireFullAccess = true
-               assertThat(preferences.requireFullAccess, equalTo(true))
-       }
-
-       @Test
-       fun `preferences retain require full access of false`() {
-               preferences.newRequireFullAccess = false
-               assertThat(preferences.requireFullAccess, equalTo(false))
-       }
-
-       @Test
-       fun `preferences return default value when require full access is set to null`() {
-               preferences.newRequireFullAccess = null
-               assertThat(preferences.requireFullAccess, equalTo(false))
-       }
-
-       @Test
-       fun `preferences start with require full access default value`() {
-               assertThat(preferences.requireFullAccess, equalTo(false))
-       }
-
-       @Test
-       fun `preferences retain fcp interface active of true`() {
-               val events = mutableListOf<FcpInterfaceActivatedEvent>()
-               eventBus.register(object {
-                       @Subscribe
-                       fun fcpInterfaceActivatedEvent(event: FcpInterfaceActivatedEvent) =
-                                       events.add(event)
-               })
-               preferences.newFcpInterfaceActive = true
-               assertThat(preferences.fcpInterfaceActive, equalTo(true))
-               assertThat(events, hasItem<FcpInterfaceActivatedEvent>(instanceOf(FcpInterfaceActivatedEvent::class.java)))
-       }
-
-       @Test
-       fun `preferences retain fcp interface active of false`() {
-               val events = mutableListOf<FcpInterfaceDeactivatedEvent>()
-               eventBus.register(object {
-                       @Subscribe
-                       fun fcpInterfaceDeactivatedEvent(event: FcpInterfaceDeactivatedEvent) =
-                                       events.add(event)
-               })
-               preferences.newFcpInterfaceActive = false
-               assertThat(preferences.fcpInterfaceActive, equalTo(false))
-               assertThat(events, hasItem<FcpInterfaceDeactivatedEvent>(instanceOf(FcpInterfaceDeactivatedEvent::class.java)))
-       }
-
-       @Test
-       fun `preferences return default value when fcp interface active is set to null`() {
-               val events = mutableListOf<FcpInterfaceDeactivatedEvent>()
-               eventBus.register(object {
-                       @Subscribe
-                       fun fcpInterfaceDeactivatedEvent(event: FcpInterfaceDeactivatedEvent) =
-                                       events.add(event)
-               })
-               preferences.newFcpInterfaceActive = null
-               assertThat(preferences.fcpInterfaceActive, equalTo(false))
-               assertThat(events, hasItem<FcpInterfaceDeactivatedEvent>(instanceOf(FcpInterfaceDeactivatedEvent::class.java)))
-       }
-
-       @Test
-       fun `preferences start with fcp interface active default value`() {
-               assertThat(preferences.fcpInterfaceActive, equalTo(false))
-       }
-
-       @Test
-       fun `preferences retain fcp full access required of no`() {
-               verifyFullAccessRequiredChangedEvent(NO)
-       }
-
-       private fun verifyFullAccessRequiredChangedEvent(set: FullAccessRequired?, expected: FullAccessRequired = set!!) {
-               val events = mutableListOf<FullAccessRequiredChanged>()
-               eventBus.register(object {
-                       @Subscribe
-                       fun fullAccessRequiredChanged(event: FullAccessRequiredChanged) =
-                                       events.add(event)
-               })
-               preferences.newFcpFullAccessRequired = set
-               assertThat(preferences.fcpFullAccessRequired, equalTo(expected))
-               assertThat(events.single().fullAccessRequired, equalTo(expected))
-       }
-
-       @Test
-       fun `preferences retain fcp full access required of writing`() {
-               verifyFullAccessRequiredChangedEvent(WRITING)
-       }
-
-       @Test
-       fun `preferences retain fcp full access required of always`() {
-               verifyFullAccessRequiredChangedEvent(ALWAYS)
-       }
-
-       @Test
-       fun `preferences return default value when fcp full access required is set to null`() {
-               verifyFullAccessRequiredChangedEvent(null, ALWAYS)
-       }
-
-       @Test
-       fun `preferences start with fcp full access required default value`() {
-               assertThat(preferences.fcpFullAccessRequired, equalTo(ALWAYS))
-       }
-
-       @Test
-       fun `default strict filtering is false`() {
-               assertThat(preferences.strictFiltering, equalTo(false))
-       }
-
-       @Test
-       fun `strict filtering can be set`() {
-               preferences.newStrictFiltering = true
-               assertThat(preferences.strictFiltering, equalTo(true))
-       }
-
-       @Test
-       fun `strict filtering returns to default on null`() {
-               preferences.newStrictFiltering = true
-               preferences.newStrictFiltering = null
-               assertThat(preferences.strictFiltering, equalTo(false))
-       }
-
-       @Test
-       fun `event is generated when strict filtering is activated`() {
-               val events = mutableListOf<StrictFilteringActivatedEvent>()
-               eventBus.register(object {
-                       @Subscribe fun strictFilteringActivatedEvent(event: StrictFilteringActivatedEvent) =
-                                       events.add(event)
-               })
-               preferences.newStrictFiltering = true
-               assertThat(events, hasItem<StrictFilteringActivatedEvent>(instanceOf(StrictFilteringActivatedEvent::class.java)))
-       }
-
-       @Test
-       fun `event is generated when strict filtering is deactivated`() {
-               val events = mutableListOf<StrictFilteringDeactivatedEvent>()
-               eventBus.register(object {
-                       @Subscribe fun strictFilteringDeactivatedEvent(event: StrictFilteringDeactivatedEvent) =
-                                       events.add(event)
-               })
-               preferences.newStrictFiltering = false
-               assertThat(events, hasItem<StrictFilteringDeactivatedEvent>(instanceOf(StrictFilteringDeactivatedEvent::class.java)))
-       }
-
-       @Test
-       fun `default strict filtering is saved as null`() {
-               verifySavedOption(nullValue()) { it.getBooleanValue("Option/StrictFiltering").value }
-       }
-
-       @Test
-       fun `activated strict filtering is saved as true`() {
-               preferences.newStrictFiltering = true
-               verifySavedOption(equalTo(true)) { it.getBooleanValue("Option/StrictFiltering").value }
-       }
-
-       @Test
-       fun `deactivated strict filtering is saved as false`() {
-               preferences.newStrictFiltering = false
-               verifySavedOption(equalTo(false)) { it.getBooleanValue("Option/StrictFiltering").value }
-       }
-
-       private fun <T> verifySavedOption(matcher: Matcher<T>, getter: (Configuration) -> T) {
-               val configuration = Configuration(MapConfigurationBackend())
-               preferences.saveTo(configuration)
-               assertThat(getter(configuration), matcher)
-       }
-
-}
-
 class PreferencesTest {
 
        @Test
index 0a7f0c7..7f57da9 100644 (file)
@@ -33,9 +33,11 @@ import net.pterodactylus.sone.test.NotParallel
 import net.pterodactylus.sone.test.assertThrows
 import net.pterodactylus.sone.test.deepMock
 import net.pterodactylus.sone.test.getInstance
+import net.pterodactylus.sone.test.isProvidedBy
 import net.pterodactylus.sone.test.isProvidedByDeepMock
 import net.pterodactylus.sone.test.isProvidedByMock
 import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.test.testPreferences
 import net.pterodactylus.sone.test.verifySingletonInstance
 import net.pterodactylus.sone.test.whenever
 import net.pterodactylus.sone.web.SessionProvider
diff --git a/src/test/kotlin/net/pterodactylus/sone/utils/DefaultOptionTest.kt b/src/test/kotlin/net/pterodactylus/sone/utils/DefaultOptionTest.kt
deleted file mode 100644 (file)
index e445b39..0000000
+++ /dev/null
@@ -1,77 +0,0 @@
-package net.pterodactylus.sone.utils
-
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.hamcrest.Matchers.nullValue
-import org.hamcrest.Matchers.sameInstance
-import org.junit.Test
-
-/**
- * Unit test for [DefaultOption].
- */
-class DefaultOptionTest {
-
-       private val defaultValue = Any()
-       private val acceptedValue = Any()
-       private val matchesAcceptedValue = { it: Any -> it == acceptedValue }
-
-       @Test
-       fun `default option returns default value when unset`() {
-               val defaultOption = DefaultOption(defaultValue)
-               assertThat(defaultOption.get(), sameInstance(defaultValue))
-       }
-
-       @Test
-       fun `default option returns null for real when unset`() {
-               val defaultOption = DefaultOption(defaultValue)
-               assertThat(defaultOption.real, nullValue())
-       }
-
-       @Test
-       fun `default option will return set value`() {
-               val defaultOption = DefaultOption(defaultValue)
-               val newValue = Any()
-               defaultOption.set(newValue)
-               assertThat(defaultOption.get(), sameInstance(newValue))
-       }
-
-       @Test
-       fun `default option with validator accepts valid values`() {
-               val defaultOption = DefaultOption(defaultValue, matchesAcceptedValue)
-               defaultOption.set(acceptedValue)
-               assertThat(defaultOption.get(), sameInstance(acceptedValue))
-       }
-
-       @Test(expected = IllegalArgumentException::class)
-       fun `default option with validator rejects invalid values`() {
-               val defaultOption = DefaultOption(defaultValue, matchesAcceptedValue)
-               defaultOption.set(Any())
-       }
-
-       @Test
-       fun `default option validates objects correctly`() {
-               val defaultOption = DefaultOption(defaultValue, matchesAcceptedValue)
-               assertThat(defaultOption.validate(acceptedValue), equalTo(true))
-               assertThat(defaultOption.validate(Any()), equalTo(false))
-       }
-
-       @Test
-       fun `setting to null will restore default value`() {
-               val defaultOption = DefaultOption(defaultValue)
-               defaultOption.set(null)
-               assertThat(defaultOption.get(), sameInstance(defaultValue))
-       }
-
-       @Test
-       fun `validate without validator will validate null`() {
-               val defaultOption = DefaultOption(defaultValue)
-               assertThat(defaultOption.validate(null), equalTo(true))
-       }
-
-       @Test
-       fun `validate with validator will validate null`() {
-               val defaultOption = DefaultOption(defaultValue, matchesAcceptedValue)
-               assertThat(defaultOption.validate(null), equalTo(true))
-       }
-
-}
index b0ad51c..7b71e3d 100644 (file)
@@ -1,6 +1,5 @@
 package net.pterodactylus.sone.web.ajax
 
-import net.pterodactylus.sone.core.DefaultPreferences
 import net.pterodactylus.sone.test.whenever
 import net.pterodactylus.sone.web.page.FreenetRequest
 import net.pterodactylus.util.web.Response
@@ -38,18 +37,13 @@ class JsonPageBaseTest : TestObjects() {
        }
 
        @Before
-       fun setupCore() {
-               whenever(core.preferences).thenReturn(DefaultPreferences(eventBus))
-       }
-
-       @Before
        fun setupFreenetRequest() {
                whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
        }
 
        @Test
        fun `page returns 403 is full access is required but request is not full access`() {
-               core.preferences.newRequireFullAccess = true
+               core.preferences.requireFullAccess = true
                page.handleRequest(freenetRequest, response)
                assertThat(response.statusCode, equalTo(403))
                assertThat(response.statusText, equalTo("Forbidden"))
index 49c0291..131ee76 100644 (file)
@@ -8,7 +8,6 @@ import freenet.support.api.HTTPRequest
 import net.pterodactylus.sone.core.Core
 import net.pterodactylus.sone.core.ElementLoader
 import net.pterodactylus.sone.core.LinkedElement
-import net.pterodactylus.sone.core.DefaultPreferences
 import net.pterodactylus.sone.core.UpdateChecker
 import net.pterodactylus.sone.data.Album
 import net.pterodactylus.sone.data.Image
@@ -34,6 +33,8 @@ import net.pterodactylus.util.web.Method.POST
 import org.mockito.ArgumentMatchers
 import java.util.*
 import javax.naming.SizeLimitExceededException
+import net.pterodactylus.sone.core.withEvents
+import net.pterodactylus.sone.test.testPreferences
 
 /**
  * Base class for tests that supplies commonly used objects.
@@ -46,7 +47,7 @@ open class TestObjects {
        var formPassword = "form-password"
        val core = mock<Core>()
        val eventBus = mock<EventBus>()
-       val preferences = DefaultPreferences(eventBus)
+       val preferences = testPreferences().withEvents(eventBus)
        val updateChecker = mock<UpdateChecker>()
        val elementLoader = mock<ElementLoader>()
        val newElements = mock<NewElements>()
index 3668755..f962e37 100644 (file)
@@ -26,7 +26,7 @@ class BookmarksPageTest : WebPageTest(::BookmarksPage) {
        @Before
        fun setupBookmarkedPostsAndPagination() {
                whenever(core.bookmarkedPosts).thenReturn(setOf(post1, post2, post3))
-               core.preferences.newPostsPerPage = 5
+               core.preferences.postsPerPage = 5
        }
 
        @Test
index 6a4a2b0..432c6be 100644 (file)
@@ -110,7 +110,7 @@ class CreateSonePageTest : WebPageTest(::CreateSonePage) {
 
        @Test
        fun `create sone is not shown in menu if full access is required but client doesn’t have full access`() {
-               core.preferences.newRequireFullAccess = true
+               core.preferences.requireFullAccess = true
                assertThat(page.isEnabled(toadletContext), equalTo(false))
        }
 
@@ -135,7 +135,7 @@ class CreateSonePageTest : WebPageTest(::CreateSonePage) {
 
        @Test
        fun `create sone is shown in menu if no sone is logged in and client has full access`() {
-               core.preferences.newRequireFullAccess = true
+               core.preferences.requireFullAccess = true
                whenever(toadletContext.isAllowedFullAccess).thenReturn(true)
                unsetCurrentSone()
                assertThat(page.isEnabled(toadletContext), equalTo(true))
index 4a2304c..254917d 100644 (file)
@@ -88,7 +88,7 @@ class ImageBrowserPageTest : WebPageTest(::ImageBrowserPage) {
 
        @Test
        fun `get request for gallery can show second page`() {
-               core.preferences.newImagesPerPage = 2
+               core.preferences.imagesPerPage = 2
                val firstSone = createSone("first album", "second album")
                addSone("sone1", firstSone)
                val secondSone = createSone("third album", "fourth album")
index 29f4a9c..46adb6d 100644 (file)
@@ -131,7 +131,7 @@ class IndexPageTest : WebPageTest({ webInterface, loaders, templateRenderer -> I
        fun `index page sets page correctly`() {
                val posts = listOf(createPost(3000), createPost(2000), createPost(1000))
                whenever(currentSone.posts).thenReturn(posts)
-               core.preferences.newPostsPerPage = 1
+               core.preferences.postsPerPage = 1
                addHttpRequestParameter("page", "2")
                page.processTemplate(freenetRequest, templateContext)
                @Suppress("UNCHECKED_CAST")
@@ -140,7 +140,7 @@ class IndexPageTest : WebPageTest({ webInterface, loaders, templateRenderer -> I
 
        @Test
        fun `index page without posts sets correct pagination`() {
-               core.preferences.newPostsPerPage = 1
+               core.preferences.postsPerPage = 1
                page.processTemplate(freenetRequest, templateContext)
                @Suppress("UNCHECKED_CAST")
                (templateContext["pagination"] as Pagination<Post>).let { pagination ->
index 067857e..8ae852d 100644 (file)
@@ -103,7 +103,7 @@ class LoginPageTest : WebPageTest(::LoginPage) {
 
        @Test
        fun `page is not enabled if full access required and request is not full access`() {
-               core.preferences.newRequireFullAccess = true
+               core.preferences.requireFullAccess = true
                assertThat(page.isEnabled(toadletContext), equalTo(false))
        }
 
@@ -120,7 +120,7 @@ class LoginPageTest : WebPageTest(::LoginPage) {
 
        @Test
        fun `page is enabled if full access required and request is full access and there is no current sone`() {
-               core.preferences.newRequireFullAccess = true
+               core.preferences.requireFullAccess = true
                unsetCurrentSone()
                whenever(toadletContext.isAllowedFullAccess).thenReturn(true)
                assertThat(page.isEnabled(toadletContext), equalTo(true))
@@ -128,7 +128,7 @@ class LoginPageTest : WebPageTest(::LoginPage) {
 
        @Test
        fun `page is not enabled if full access required and request is full access but there is a current sone`() {
-               core.preferences.newRequireFullAccess = true
+               core.preferences.requireFullAccess = true
                whenever(toadletContext.isAllowedFullAccess).thenReturn(true)
                assertThat(page.isEnabled(toadletContext), equalTo(false))
        }
index fae95d2..96c847e 100644 (file)
@@ -38,7 +38,7 @@ class LogoutPageTest : WebPageTest(::LogoutPage) {
 
        @Test
        fun `page is not enabled if sone requires full access and request does not have full access`() {
-               core.preferences.newRequireFullAccess = true
+               core.preferences.requireFullAccess = true
                assertThat(page.isEnabled(toadletContext), equalTo(false))
        }
 
@@ -62,7 +62,7 @@ class LogoutPageTest : WebPageTest(::LogoutPage) {
 
        @Test
        fun `page is enabled if full access is required and present and sone is logged in and there is more than one sone`() {
-               core.preferences.newRequireFullAccess = true
+               core.preferences.requireFullAccess = true
                whenever(toadletContext.isAllowedFullAccess).thenReturn(true)
                whenever(core.localSones).thenReturn(listOf(currentSone, currentSone))
                assertThat(page.isEnabled(toadletContext), equalTo(true))
index 3cf2a75..f5cf297 100644 (file)
@@ -21,7 +21,7 @@ class NewPageTest : WebPageTest() {
 
        @Before
        fun setupNumberOfPostsPerPage() {
-               webInterface.core.preferences.newPostsPerPage = 5
+               webInterface.core.preferences.postsPerPage = 5
        }
 
        @Test
@@ -61,7 +61,7 @@ class NewPageTest : WebPageTest() {
        @Test
        @Suppress("UNCHECKED_CAST")
        fun `posts are paginated properly`() {
-               webInterface.core.preferences.newPostsPerPage = 2
+               webInterface.core.preferences.postsPerPage = 2
                val posts = listOf(mock<Post>().withTime(2000), mock<Post>().withTime(3000), mock<Post>().withTime(1000))
                whenever(newElements.newPosts).thenReturn(posts)
                verifyNoRedirect {
@@ -72,7 +72,7 @@ class NewPageTest : WebPageTest() {
        @Test
        @Suppress("UNCHECKED_CAST")
        fun `posts are paginated properly on second page`() {
-               webInterface.core.preferences.newPostsPerPage = 2
+               webInterface.core.preferences.postsPerPage = 2
                addHttpRequestParameter("page", "1")
                val posts = listOf(mock<Post>().withTime(2000), mock<Post>().withTime(3000), mock<Post>().withTime(1000))
                whenever(newElements.newPosts).thenReturn(posts)
index d679e85..34379d8 100644 (file)
@@ -19,15 +19,15 @@ class OptionsPageTest : WebPageTest(::OptionsPage) {
 
        @Before
        fun setupPreferences() {
-               core.preferences.newInsertionDelay = 1
-               core.preferences.newCharactersPerPost = 50
-               core.preferences.newFcpFullAccessRequired = WRITING
-               core.preferences.newImagesPerPage = 4
-               core.preferences.newFcpInterfaceActive = true
-               core.preferences.newRequireFullAccess = true
-               core.preferences.newPostCutOffLength = 51
-               core.preferences.newPostsPerPage = 10
-               core.preferences.newStrictFiltering = true
+               core.preferences.insertionDelay = 1
+               core.preferences.charactersPerPost = 50
+               core.preferences.fcpFullAccessRequired = WRITING
+               core.preferences.imagesPerPage = 4
+               core.preferences.fcpInterfaceActive = true
+               core.preferences.requireFullAccess = true
+               core.preferences.postCutOffLength = 51
+               core.preferences.postsPerPage = 10
+               core.preferences.strictFiltering = true
        }
 
        @Before
index d7e5890..8f76059 100644 (file)
@@ -276,7 +276,7 @@ class SearchPageTest : WebPageTest({ webInterface, loaders, templateRenderer ->
 
        @Test
        fun `sone hits are paginated correctly`() {
-               core.preferences.newPostsPerPage = 2
+               core.preferences.postsPerPage = 2
                val sones = listOf(createSone("1Sone"), createSone("Other1"), createSone("22Sone"), createSone("333Sone"), createSone("Other2"))
                                .onEach { addSone(it.id, it) }
                addHttpRequestParameter("query", "sone")
@@ -288,7 +288,7 @@ class SearchPageTest : WebPageTest({ webInterface, loaders, templateRenderer ->
 
        @Test
        fun `sone hits page 2 is shown correctly`() {
-               core.preferences.newPostsPerPage = 2
+               core.preferences.postsPerPage = 2
                val sones = listOf(createSone("1Sone"), createSone("Other1"), createSone("22Sone"), createSone("333Sone"), createSone("Other2"))
                                .onEach { addSone(it.id, it) }
                addHttpRequestParameter("query", "sone")
@@ -301,7 +301,7 @@ class SearchPageTest : WebPageTest({ webInterface, loaders, templateRenderer ->
 
        @Test
        fun `post hits are paginated correctly`() {
-               core.preferences.newPostsPerPage = 2
+               core.preferences.postsPerPage = 2
                val sones = listOf(createSoneWithPost("match1", "1Sone"), createSoneWithPost("no-match1", "Other1"), createSoneWithPost("match2", "22Sone"), createSoneWithPost("match3", "333Sone"), createSoneWithPost("no-match2", "Other2"))
                addHttpRequestParameter("query", "sone")
                verifyNoRedirect {
@@ -312,7 +312,7 @@ class SearchPageTest : WebPageTest({ webInterface, loaders, templateRenderer ->
 
        @Test
        fun `post hits page 2 is shown correctly`() {
-               core.preferences.newPostsPerPage = 2
+               core.preferences.postsPerPage = 2
                val sones = listOf(createSoneWithPost("match1", "1Sone"), createSoneWithPost("no-match1", "Other1"), createSoneWithPost("match2", "22Sone"), createSoneWithPost("match3", "333Sone"), createSoneWithPost("no-match2", "Other2"))
                addHttpRequestParameter("query", "sone")
                addHttpRequestParameter("postPage", "1")
index cffd478..400bfc3 100644 (file)
@@ -67,7 +67,7 @@ class SoneTemplatePageTest : WebPageTest({ webInterface, loaders, templateRender
 
        @Test
        fun `preferences are set in template context`() {
-               verifyVariableIsSet("preferences", preferences)
+               verifyVariableMatches("preferences", notNullValue())
        }
 
        @Test
@@ -182,7 +182,7 @@ class SoneTemplatePageTest : WebPageTest({ webInterface, loaders, templateRender
 
        @Test
        fun `page is disabled if full access is required but request does not have full access`() {
-               core.preferences.newRequireFullAccess = true
+               core.preferences.requireFullAccess = true
                assertThat(page.isEnabled(toadletContext), equalTo(false))
        }
 
@@ -199,7 +199,7 @@ class SoneTemplatePageTest : WebPageTest({ webInterface, loaders, templateRender
 
        @Test
        fun `page is enabled if full access is required and request has full access and login is required and there is a current sone`() {
-               core.preferences.newRequireFullAccess = true
+               core.preferences.requireFullAccess = true
                whenever(toadletContext.isAllowedFullAccess).thenReturn(true)
                assertThat(page.isEnabled(toadletContext), equalTo(true))
        }
index 6e97e36..a9f72f5 100644 (file)
@@ -31,7 +31,7 @@ class ViewSonePageTest : WebPageTest(::ViewSonePage) {
        fun setup() {
                whenever(currentSone.posts).thenReturn(mutableListOf(post2, post1))
                whenever(core.getDirectedPosts("sone-id")).thenReturn(setOf(directed1, directed2))
-               core.preferences.newPostsPerPage = 2
+               core.preferences.postsPerPage = 2
        }
 
        @Test
index 98c8156..79ff0c4 100644 (file)
@@ -29,6 +29,7 @@ import java.net.*
 import java.nio.charset.*
 import java.util.*
 import kotlin.text.Charsets.UTF_8
+import net.pterodactylus.sone.test.testPreferences
 
 /**
  * Base class for web page tests.
@@ -41,7 +42,7 @@ open class WebPageTest(pageSupplier: (WebInterface, Loaders, TemplateRenderer) -
        val webInterface = deepMock<WebInterface>()
        val core = webInterface.core
        val eventBus = mock<EventBus>()
-       val preferences = DefaultPreferences(eventBus)
+       val preferences = testPreferences().withEvents(eventBus)
 
        open val page by lazy { pageSupplier(webInterface, loaders, templateRenderer) }