From 0fef481d480737528491fc0b3b8487eb1612f955 Mon Sep 17 00:00:00 2001 From: Luck Date: Sun, 22 Dec 2024 09:50:25 +0000 Subject: [PATCH] Add more unit tests --- .../dataorder/DataTypeFilterFunction.java | 2 +- common/build.gradle | 2 +- .../implementation/ApiMetaStackFactory.java | 4 +- .../cacheddata/AbstractCachedDataManager.java | 2 +- .../common/cacheddata/UsageTracked.java | 12 +- .../metastack}/FluentMetaStackElement.java | 2 +- .../metastack}/SimpleMetaStackDefinition.java | 2 +- .../metastack}/StandardStackElements.java | 14 +- .../cacheddata/result/AbstractResult.java | 57 ++++ .../cacheddata/result/IntegerResult.java | 24 +- .../cacheddata/result/StringResult.java | 23 +- .../cacheddata/result/TristateResult.java | 22 +- .../command/abstraction/ChildCommand.java | 3 +- .../abstraction/GenericChildCommand.java | 3 +- .../command/abstraction/SingleCommand.java | 3 +- .../luckperms/common/config/ConfigKeys.java | 32 +-- .../dependencies/DependencyRepository.java | 2 +- .../common/node/utils/ShorthandParser.java | 5 + .../plugin/AbstractLuckPermsPlugin.java | 5 +- .../luckperms/common/query/FlagUtils.java | 2 +- .../common/query/QueryOptionsImpl.java | 6 +- .../common/sender/AbstractSender.java | 3 +- .../treeview/AsyncPermissionRegistry.java | 70 +++++ .../common/treeview/PermissionRegistry.java | 51 ++-- .../common/util/DurationFormatter.java | 3 +- .../common/verbose/VerboseListener.java | 3 +- .../cacheddata/MetaAccumulatorTest.java | 168 ++++++++++++ .../cacheddata/MetaStackAccumulatorTest.java | 104 ++++++++ .../cacheddata/MetaStackElementTest.java | 243 ++++++++++++++++++ .../cacheddata/MetaValueSelectorTest.java | 81 ++++++ .../common/cacheddata/UsageTrackedTest.java | 54 ++++ .../common/filter/FilterMongoTest.java | 17 +- .../common/filter/FilterSqlTest.java | 16 +- .../common/node/NodeCommandFactoryTest.java | 71 +++++ .../common/node/NodeMatcherTest.java | 111 ++++++++ .../luckperms/common/node/NodeParseTest.java | 19 +- .../lucko/luckperms/common/node/NodeTest.java | 15 ++ .../common/node/ShorthandParserTest.java | 16 ++ .../common/query/DataSelectorTest.java | 84 ++++++ .../common/query/QueryOptionsTest.java | 62 +++++ .../treeview/PermissionRegistryTest.java | 81 ++++++ .../luckperms/fabric/LPFabricPlugin.java | 4 +- .../messaging/PluginMessageMessenger.java | 1 + .../messaging/PluginMessageMessenger.java | 1 + .../model/calculated/CalculatedSubject.java | 3 +- .../CalculatedSubjectCachedDataManager.java | 4 +- 46 files changed, 1358 insertions(+), 154 deletions(-) rename common/src/main/java/me/lucko/luckperms/common/{metastacking => cacheddata/metastack}/FluentMetaStackElement.java (98%) rename common/src/main/java/me/lucko/luckperms/common/{metastacking => cacheddata/metastack}/SimpleMetaStackDefinition.java (98%) rename common/src/main/java/me/lucko/luckperms/common/{metastacking => cacheddata/metastack}/StandardStackElements.java (95%) create mode 100644 common/src/main/java/me/lucko/luckperms/common/cacheddata/result/AbstractResult.java create mode 100644 common/src/main/java/me/lucko/luckperms/common/treeview/AsyncPermissionRegistry.java create mode 100644 common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaAccumulatorTest.java create mode 100644 common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaStackAccumulatorTest.java create mode 100644 common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaStackElementTest.java create mode 100644 common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaValueSelectorTest.java create mode 100644 common/src/test/java/me/lucko/luckperms/common/cacheddata/UsageTrackedTest.java create mode 100644 common/src/test/java/me/lucko/luckperms/common/node/NodeCommandFactoryTest.java create mode 100644 common/src/test/java/me/lucko/luckperms/common/node/NodeMatcherTest.java create mode 100644 common/src/test/java/me/lucko/luckperms/common/query/DataSelectorTest.java create mode 100644 common/src/test/java/me/lucko/luckperms/common/query/QueryOptionsTest.java create mode 100644 common/src/test/java/me/lucko/luckperms/common/treeview/PermissionRegistryTest.java diff --git a/api/src/main/java/net/luckperms/api/query/dataorder/DataTypeFilterFunction.java b/api/src/main/java/net/luckperms/api/query/dataorder/DataTypeFilterFunction.java index 10c8c3b73..0ad457547 100644 --- a/api/src/main/java/net/luckperms/api/query/dataorder/DataTypeFilterFunction.java +++ b/api/src/main/java/net/luckperms/api/query/dataorder/DataTypeFilterFunction.java @@ -48,7 +48,7 @@ public interface DataTypeFilterFunction { /** * Creates a {@link DataTypeFilterFunction} that always returns the given - * {@code predicate}. + * {@code predicate} (commonly one of the values in {@link DataTypeFilter}). * * @param predicate the predicate * @return the data type filter function diff --git a/common/build.gradle b/common/build.gradle index 9db5a047f..673e127ab 100644 --- a/common/build.gradle +++ b/common/build.gradle @@ -19,7 +19,7 @@ dependencies { testImplementation 'org.junit.jupiter:junit-jupiter-api:5.9.1' testImplementation 'org.junit.jupiter:junit-jupiter-engine:5.9.1' testImplementation 'org.junit.jupiter:junit-jupiter-params:5.9.1' - testImplementation "org.testcontainers:junit-jupiter:1.19.8" + testImplementation 'org.testcontainers:junit-jupiter:1.20.4' testImplementation 'org.mockito:mockito-core:5.11.0' testImplementation 'org.mockito:mockito-junit-jupiter:5.11.0' testImplementation 'com.h2database:h2:2.1.214' diff --git a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiMetaStackFactory.java b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiMetaStackFactory.java index 52f0ec8b4..8abcf2bec 100644 --- a/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiMetaStackFactory.java +++ b/common/src/main/java/me/lucko/luckperms/common/api/implementation/ApiMetaStackFactory.java @@ -26,8 +26,8 @@ package me.lucko.luckperms.common.api.implementation; import com.google.common.collect.ImmutableList; -import me.lucko.luckperms.common.metastacking.SimpleMetaStackDefinition; -import me.lucko.luckperms.common.metastacking.StandardStackElements; +import me.lucko.luckperms.common.cacheddata.metastack.SimpleMetaStackDefinition; +import me.lucko.luckperms.common.cacheddata.metastack.StandardStackElements; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import net.luckperms.api.metastacking.DuplicateRemovalFunction; import net.luckperms.api.metastacking.MetaStackDefinition; diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java index 61780b7b9..66e54f300 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/AbstractCachedDataManager.java @@ -193,7 +193,7 @@ public abstract class AbstractCachedDataManager implements CachedDataManager { } public void cleanup() { - this.cache.values().removeIf(value -> ((UsageTracked) value).usedSince(TimeUnit.MINUTES.toMillis(2))); + this.cache.values().removeIf(value -> !((UsageTracked) value).usedInTheLast(2, TimeUnit.MINUTES)); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/UsageTracked.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/UsageTracked.java index 602d9cacd..250130ddb 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/UsageTracked.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/UsageTracked.java @@ -25,14 +25,20 @@ package me.lucko.luckperms.common.cacheddata; +import com.google.common.annotations.VisibleForTesting; + +import java.util.concurrent.TimeUnit; + public abstract class UsageTracked { - private long lastUsed = System.currentTimeMillis(); + + @VisibleForTesting + protected long lastUsed = System.currentTimeMillis(); public void recordUsage() { this.lastUsed = System.currentTimeMillis(); } - public boolean usedSince(long duration) { - return this.lastUsed > System.currentTimeMillis() - duration; + public boolean usedInTheLast(long duration, TimeUnit unit) { + return this.lastUsed > System.currentTimeMillis() - unit.toMillis(duration); } } diff --git a/common/src/main/java/me/lucko/luckperms/common/metastacking/FluentMetaStackElement.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/metastack/FluentMetaStackElement.java similarity index 98% rename from common/src/main/java/me/lucko/luckperms/common/metastacking/FluentMetaStackElement.java rename to common/src/main/java/me/lucko/luckperms/common/cacheddata/metastack/FluentMetaStackElement.java index 09973bb2b..c7d5ce48c 100644 --- a/common/src/main/java/me/lucko/luckperms/common/metastacking/FluentMetaStackElement.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/metastack/FluentMetaStackElement.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.metastacking; +package me.lucko.luckperms.common.cacheddata.metastack; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; diff --git a/common/src/main/java/me/lucko/luckperms/common/metastacking/SimpleMetaStackDefinition.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/metastack/SimpleMetaStackDefinition.java similarity index 98% rename from common/src/main/java/me/lucko/luckperms/common/metastacking/SimpleMetaStackDefinition.java rename to common/src/main/java/me/lucko/luckperms/common/cacheddata/metastack/SimpleMetaStackDefinition.java index fc526b9cd..bda2e9265 100644 --- a/common/src/main/java/me/lucko/luckperms/common/metastacking/SimpleMetaStackDefinition.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/metastack/SimpleMetaStackDefinition.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.metastacking; +package me.lucko.luckperms.common.cacheddata.metastack; import com.google.common.collect.ImmutableList; import net.luckperms.api.metastacking.DuplicateRemovalFunction; diff --git a/common/src/main/java/me/lucko/luckperms/common/metastacking/StandardStackElements.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/metastack/StandardStackElements.java similarity index 95% rename from common/src/main/java/me/lucko/luckperms/common/metastacking/StandardStackElements.java rename to common/src/main/java/me/lucko/luckperms/common/cacheddata/metastack/StandardStackElements.java index 508ff6e14..a67ee0c87 100644 --- a/common/src/main/java/me/lucko/luckperms/common/metastacking/StandardStackElements.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/metastack/StandardStackElements.java @@ -23,7 +23,7 @@ * SOFTWARE. */ -package me.lucko.luckperms.common.metastacking; +package me.lucko.luckperms.common.cacheddata.metastack; import me.lucko.luckperms.common.model.Track; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; @@ -220,8 +220,11 @@ public final class StandardStackElements { @Override public boolean shouldAccumulate(@NonNull ChatMetaType type, @NonNull ChatMetaNode node, @Nullable ChatMetaNode current) { Track track = this.plugin.getTrackManager().getIfLoaded(this.trackName); + if (track == null) { + return false; + } PermissionHolder.Identifier origin = node.metadata(InheritanceOriginMetadata.KEY).getOrigin(); - return track != null && origin.getType().equals(PermissionHolder.Identifier.GROUP_TYPE) && track.containsGroup(origin.getName()); + return origin.getType().equals(PermissionHolder.Identifier.GROUP_TYPE) && track.containsGroup(origin.getName()); } @Override @@ -250,8 +253,11 @@ public final class StandardStackElements { @Override public boolean shouldAccumulate(@NonNull ChatMetaType type, @NonNull ChatMetaNode node, @Nullable ChatMetaNode current) { Track track = this.plugin.getTrackManager().getIfLoaded(this.trackName); + if (track == null) { + return false; + } PermissionHolder.Identifier origin = node.metadata(InheritanceOriginMetadata.KEY).getOrigin(); - return track != null && !track.containsGroup(origin.getName()); + return !(origin.getType().equals(PermissionHolder.Identifier.GROUP_TYPE) && track.containsGroup(origin.getName())); } @Override @@ -305,7 +311,7 @@ public final class StandardStackElements { @Override public boolean shouldAccumulate(@NonNull ChatMetaType type, @NonNull ChatMetaNode node, @Nullable ChatMetaNode current) { PermissionHolder.Identifier origin = node.metadata(InheritanceOriginMetadata.KEY).getOrigin(); - return !this.groupName.equals(origin.getName()); + return !(origin.getType().equals(PermissionHolder.Identifier.GROUP_TYPE) && this.groupName.equals(origin.getName())); } @Override diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/AbstractResult.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/AbstractResult.java new file mode 100644 index 000000000..5d5199de2 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/AbstractResult.java @@ -0,0 +1,57 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.cacheddata.result; + +import net.luckperms.api.cacheddata.Result; +import net.luckperms.api.node.Node; +import org.checkerframework.checker.nullness.qual.Nullable; + +public abstract class AbstractResult> implements Result { + + /** The node that caused the result */ + protected final N node; + /** A reference to another result that this one overrides */ + protected S overriddenResult; + + public AbstractResult(N node, S overriddenResult) { + this.node = node; + this.overriddenResult = overriddenResult; + } + + @Override + public final @Nullable N node() { + return this.node; + } + + public final @Nullable S overriddenResult() { + return this.overriddenResult; + } + + public final void setOverriddenResult(S overriddenResult) { + this.overriddenResult = overriddenResult; + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/IntegerResult.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/IntegerResult.java index 3546484d4..e7230b331 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/IntegerResult.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/IntegerResult.java @@ -25,30 +25,23 @@ package me.lucko.luckperms.common.cacheddata.result; -import net.luckperms.api.cacheddata.Result; import net.luckperms.api.node.Node; import net.luckperms.api.node.types.WeightNode; import org.checkerframework.checker.nullness.qual.NonNull; -import org.checkerframework.checker.nullness.qual.Nullable; /** * Represents the result of an integer meta lookup * * @param the node type */ -public final class IntegerResult implements Result { +public final class IntegerResult extends AbstractResult> { /** The result */ private final int result; - /** The node that caused the result */ - private final N node; - /** A reference to another result that this one overrides */ - private IntegerResult overriddenResult; public IntegerResult(int result, N node, IntegerResult overriddenResult) { + super(node, overriddenResult); this.result = result; - this.node = node; - this.overriddenResult = overriddenResult; } @Override @@ -73,19 +66,6 @@ public final class IntegerResult implements Result { } } - @Override - public @Nullable N node() { - return this.node; - } - - public @Nullable IntegerResult overriddenResult() { - return this.overriddenResult; - } - - public void setOverriddenResult(IntegerResult overriddenResult) { - this.overriddenResult = overriddenResult; - } - public boolean isNull() { return this == NULL_RESULT; } diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java index 69121af17..204117608 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/StringResult.java @@ -25,7 +25,6 @@ package me.lucko.luckperms.common.cacheddata.result; -import net.luckperms.api.cacheddata.Result; import net.luckperms.api.node.Node; import net.luckperms.api.node.types.ChatMetaNode; import net.luckperms.api.node.types.MetaNode; @@ -36,19 +35,14 @@ import org.checkerframework.checker.nullness.qual.Nullable; * * @param the node type */ -public final class StringResult implements Result { +public final class StringResult extends AbstractResult> { /** The result, nullable */ private final String result; - /** The node that caused the result */ - private final N node; - /** A reference to another result that this one overrides */ - private StringResult overriddenResult; public StringResult(String result, N node, StringResult overriddenResult) { + super(node, overriddenResult); this.result = result; - this.node = node; - this.overriddenResult = overriddenResult; } @Override @@ -56,19 +50,6 @@ public final class StringResult implements Result { return this.result; } - @Override - public @Nullable N node() { - return this.node; - } - - public @Nullable StringResult overriddenResult() { - return this.overriddenResult; - } - - public void setOverriddenResult(StringResult overriddenResult) { - this.overriddenResult = overriddenResult; - } - public StringResult copy() { return new StringResult<>(this.result, this.node, this.overriddenResult); } diff --git a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java index cb4c6817c..cd8aceab5 100644 --- a/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java +++ b/common/src/main/java/me/lucko/luckperms/common/cacheddata/result/TristateResult.java @@ -27,7 +27,6 @@ package me.lucko.luckperms.common.cacheddata.result; import me.lucko.luckperms.common.calculator.PermissionCalculator; import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; -import net.luckperms.api.cacheddata.Result; import net.luckperms.api.node.Node; import net.luckperms.api.util.Tristate; import org.checkerframework.checker.nullness.qual.NonNull; @@ -36,20 +35,16 @@ import org.checkerframework.checker.nullness.qual.Nullable; /** * Represents the result of a {@link PermissionCalculator} lookup. */ -public final class TristateResult implements Result { +public final class TristateResult extends AbstractResult { /** The result */ private final Tristate result; - /** The node that caused the result */ - private final Node node; /** The permission processor that provided the result */ private final Class processorClass; - /** A reference to another result that this one overrides */ - private TristateResult overriddenResult; private TristateResult(Tristate result, Node node, Class processorClass) { + super(node, null); this.result = result; - this.node = node; this.processorClass = processorClass; } @@ -58,11 +53,6 @@ public final class TristateResult implements Result { return this.result; } - @Override - public @Nullable Node node() { - return this.node; - } - public @Nullable Class processorClass() { return this.processorClass; } @@ -79,14 +69,6 @@ public final class TristateResult implements Result { } } - public @Nullable TristateResult overriddenResult() { - return this.overriddenResult; - } - - public void setOverriddenResult(TristateResult overriddenResult) { - this.overriddenResult = overriddenResult; - } - @Override public String toString() { return "TristateResult(" + diff --git a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/ChildCommand.java b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/ChildCommand.java index 2e7672ddf..ea894bfbc 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/ChildCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/ChildCommand.java @@ -31,6 +31,7 @@ import me.lucko.luckperms.common.command.spec.CommandSpec; import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.sender.Sender; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.JoinConfiguration; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; @@ -66,7 +67,7 @@ public abstract class ChildCommand extends Command { .collect(Collectors.toList()); builder.append(Component.text(" - ", NamedTextColor.DARK_AQUA)) - .append(Component.join(Component.space(), argUsages)) + .append(Component.join(JoinConfiguration.separator(Component.space()), argUsages)) .build(); } diff --git a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/GenericChildCommand.java b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/GenericChildCommand.java index b39997250..55db3cb78 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/GenericChildCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/GenericChildCommand.java @@ -35,6 +35,7 @@ import me.lucko.luckperms.common.model.PermissionHolder; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.JoinConfiguration; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; @@ -114,7 +115,7 @@ public abstract class GenericChildCommand { .collect(Collectors.toList()); builder.append(Component.text(" - ", NamedTextColor.DARK_AQUA)) - .append(Component.join(Component.space(), argUsages)) + .append(Component.join(JoinConfiguration.separator(Component.space()), argUsages)) .build(); } diff --git a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/SingleCommand.java b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/SingleCommand.java index af83f9a2d..81ac555b7 100644 --- a/common/src/main/java/me/lucko/luckperms/common/command/abstraction/SingleCommand.java +++ b/common/src/main/java/me/lucko/luckperms/common/command/abstraction/SingleCommand.java @@ -33,6 +33,7 @@ import me.lucko.luckperms.common.locale.Message; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.sender.Sender; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.JoinConfiguration; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.format.NamedTextColor; @@ -70,7 +71,7 @@ public abstract class SingleCommand extends Command { .collect(Collectors.toList()); builder.append(Component.text(" - ", NamedTextColor.DARK_AQUA)) - .append(Component.join(Component.space(), argUsages)) + .append(Component.join(JoinConfiguration.separator(Component.space()), argUsages)) .build(); } diff --git a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java index 495f3296f..3a394545b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java +++ b/common/src/main/java/me/lucko/luckperms/common/config/ConfigKeys.java @@ -28,14 +28,14 @@ package me.lucko.luckperms.common.config; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Maps; +import me.lucko.luckperms.common.cacheddata.metastack.SimpleMetaStackDefinition; +import me.lucko.luckperms.common.cacheddata.metastack.StandardStackElements; import me.lucko.luckperms.common.cacheddata.type.SimpleMetaValueSelector; import me.lucko.luckperms.common.config.generic.KeyedConfiguration; import me.lucko.luckperms.common.config.generic.key.ConfigKey; import me.lucko.luckperms.common.config.generic.key.SimpleConfigKey; import me.lucko.luckperms.common.context.calculator.WorldNameRewriter; import me.lucko.luckperms.common.graph.TraversalAlgorithm; -import me.lucko.luckperms.common.metastacking.SimpleMetaStackDefinition; -import me.lucko.luckperms.common.metastacking.StandardStackElements; import me.lucko.luckperms.common.model.PrimaryGroupHolder; import me.lucko.luckperms.common.model.User; import me.lucko.luckperms.common.query.QueryOptionsBuilderImpl; @@ -370,16 +370,16 @@ public final class ConfigKeys { /** * Creates a new prefix MetaStack element based upon the configured values. */ - public static final ConfigKey PREFIX_FORMATTING_OPTIONS = key(l -> { - List format = l.getStringList("meta-formatting.prefix.format", new ArrayList<>()); + public static final ConfigKey PREFIX_FORMATTING_OPTIONS = key(c -> { + List format = c.getStringList("meta-formatting.prefix.format", new ArrayList<>()); if (format.isEmpty()) { format.add("highest"); } - String startSpacer = l.getString("meta-formatting.prefix.start-spacer", ""); - String middleSpacer = l.getString("meta-formatting.prefix.middle-spacer", " "); - String endSpacer = l.getString("meta-formatting.prefix.end-spacer", ""); + String startSpacer = c.getString("meta-formatting.prefix.start-spacer", ""); + String middleSpacer = c.getString("meta-formatting.prefix.middle-spacer", " "); + String endSpacer = c.getString("meta-formatting.prefix.end-spacer", ""); DuplicateRemovalFunction duplicateRemovalFunction; - switch (l.getString("meta-formatting.prefix.duplicates", "").toLowerCase(Locale.ROOT)) { + switch (c.getString("meta-formatting.prefix.duplicates", "").toLowerCase(Locale.ROOT)) { case "first-only": duplicateRemovalFunction = DuplicateRemovalFunction.FIRST_ONLY; break; @@ -391,22 +391,22 @@ public final class ConfigKeys { break; } - return new SimpleMetaStackDefinition(StandardStackElements.parseList(l.getPlugin(), format), duplicateRemovalFunction, startSpacer, middleSpacer, endSpacer); + return new SimpleMetaStackDefinition(StandardStackElements.parseList(c.getPlugin(), format), duplicateRemovalFunction, startSpacer, middleSpacer, endSpacer); }); /** * Creates a new suffix MetaStack element based upon the configured values. */ - public static final ConfigKey SUFFIX_FORMATTING_OPTIONS = key(l -> { - List format = l.getStringList("meta-formatting.suffix.format", new ArrayList<>()); + public static final ConfigKey SUFFIX_FORMATTING_OPTIONS = key(c -> { + List format = c.getStringList("meta-formatting.suffix.format", new ArrayList<>()); if (format.isEmpty()) { format.add("highest"); } - String startSpacer = l.getString("meta-formatting.suffix.start-spacer", ""); - String middleSpacer = l.getString("meta-formatting.suffix.middle-spacer", " "); - String endSpacer = l.getString("meta-formatting.suffix.end-spacer", ""); + String startSpacer = c.getString("meta-formatting.suffix.start-spacer", ""); + String middleSpacer = c.getString("meta-formatting.suffix.middle-spacer", " "); + String endSpacer = c.getString("meta-formatting.suffix.end-spacer", ""); DuplicateRemovalFunction duplicateRemovalFunction; - switch (l.getString("meta-formatting.suffix.duplicates", "").toLowerCase(Locale.ROOT)) { + switch (c.getString("meta-formatting.suffix.duplicates", "").toLowerCase(Locale.ROOT)) { case "first-only": duplicateRemovalFunction = DuplicateRemovalFunction.FIRST_ONLY; break; @@ -418,7 +418,7 @@ public final class ConfigKeys { break; } - return new SimpleMetaStackDefinition(StandardStackElements.parseList(l.getPlugin(), format), duplicateRemovalFunction, startSpacer, middleSpacer, endSpacer); + return new SimpleMetaStackDefinition(StandardStackElements.parseList(c.getPlugin(), format), duplicateRemovalFunction, startSpacer, middleSpacer, endSpacer); }); /** diff --git a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRepository.java b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRepository.java index 7dcdd4ea5..5d58cf71b 100644 --- a/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRepository.java +++ b/common/src/main/java/me/lucko/luckperms/common/dependencies/DependencyRepository.java @@ -138,7 +138,7 @@ public enum DependencyRepository { } /** - * Downloads the the {@code dependency} to the {@code file}, ensuring the + * Downloads the {@code dependency} to the {@code file}, ensuring the * downloaded bytes match the checksum. * * @param dependency the dependency to download diff --git a/common/src/main/java/me/lucko/luckperms/common/node/utils/ShorthandParser.java b/common/src/main/java/me/lucko/luckperms/common/node/utils/ShorthandParser.java index 86c67720d..0858d4a69 100644 --- a/common/src/main/java/me/lucko/luckperms/common/node/utils/ShorthandParser.java +++ b/common/src/main/java/me/lucko/luckperms/common/node/utils/ShorthandParser.java @@ -210,12 +210,17 @@ public enum ShorthandParser { * Implements an iterator over a given range of ints. */ private abstract static class RangeIterator implements Iterator { + private static final int MAX_RANGE = 250; + private final int max; private int next; RangeIterator(int a, int b) { this.max = Math.max(a, b); this.next = Math.min(a, b); + if ((this.max - this.next) > MAX_RANGE) { + throw new IllegalArgumentException("Range too large"); + } } protected abstract String toString(int i); diff --git a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java index 74e279789..dd2e25c12 100644 --- a/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java +++ b/common/src/main/java/me/lucko/luckperms/common/plugin/AbstractLuckPermsPlugin.java @@ -61,6 +61,7 @@ import me.lucko.luckperms.common.storage.misc.DataConstraints; import me.lucko.luckperms.common.tasks.CacheHousekeepingTask; import me.lucko.luckperms.common.tasks.ExpireTemporaryTask; import me.lucko.luckperms.common.tasks.SyncTask; +import me.lucko.luckperms.common.treeview.AsyncPermissionRegistry; import me.lucko.luckperms.common.treeview.PermissionRegistry; import me.lucko.luckperms.common.verbose.VerboseHandler; import me.lucko.luckperms.common.webeditor.socket.WebEditorSocket; @@ -92,7 +93,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { // init during load private DependencyManager dependencyManager; private TranslationManager translationManager; - private PermissionRegistry permissionRegistry; + private AsyncPermissionRegistry permissionRegistry; private VerboseHandler verboseHandler; // init during enable @@ -128,7 +129,7 @@ public abstract class AbstractLuckPermsPlugin implements LuckPermsPlugin { this.translationManager.reload(); // load some utilities early - this.permissionRegistry = new PermissionRegistry(getBootstrap().getScheduler()); + this.permissionRegistry = new AsyncPermissionRegistry(getBootstrap().getScheduler()); this.verboseHandler = new VerboseHandler(getBootstrap().getScheduler()); } diff --git a/common/src/main/java/me/lucko/luckperms/common/query/FlagUtils.java b/common/src/main/java/me/lucko/luckperms/common/query/FlagUtils.java index 6b0be4495..9a6766e31 100644 --- a/common/src/main/java/me/lucko/luckperms/common/query/FlagUtils.java +++ b/common/src/main/java/me/lucko/luckperms/common/query/FlagUtils.java @@ -54,7 +54,7 @@ final class FlagUtils { private static byte toByte0(Set settings) { byte b = 0; for (Flag setting : settings) { - b |= 1 << setting.ordinal(); + b |= (byte) (1 << setting.ordinal()); } return b; } diff --git a/common/src/main/java/me/lucko/luckperms/common/query/QueryOptionsImpl.java b/common/src/main/java/me/lucko/luckperms/common/query/QueryOptionsImpl.java index ce25b26da..0fe3d6d7e 100644 --- a/common/src/main/java/me/lucko/luckperms/common/query/QueryOptionsImpl.java +++ b/common/src/main/java/me/lucko/luckperms/common/query/QueryOptionsImpl.java @@ -56,7 +56,7 @@ public class QueryOptionsImpl implements QueryOptions { // computed based on state above private final int hashCode; private Set flagsSet = null; - private final ContextSatisfyMode contextSatisfyMode; + private final ContextSatisfyMode overrideContextSatisfyMode; QueryOptionsImpl(QueryMode mode, @Nullable ImmutableContextSet context, byte flags, @Nullable Map, Object> options) { this.mode = mode; @@ -65,7 +65,7 @@ public class QueryOptionsImpl implements QueryOptions { this.options = options == null ? null : ImmutableMap.copyOf(options); this.hashCode = calculateHashCode(); - this.contextSatisfyMode = options == null ? null : (ContextSatisfyMode) options.get(ContextSatisfyMode.KEY); + this.overrideContextSatisfyMode = options == null ? null : (ContextSatisfyMode) options.get(ContextSatisfyMode.KEY); } @Override @@ -119,7 +119,7 @@ public class QueryOptionsImpl implements QueryOptions { public boolean satisfies(@NonNull ContextSet contextSet, @NonNull ContextSatisfyMode defaultContextSatisfyMode) { switch (this.mode) { case CONTEXTUAL: - return contextSet.isSatisfiedBy(this.context, this.contextSatisfyMode == null ? defaultContextSatisfyMode : this.contextSatisfyMode); + return contextSet.isSatisfiedBy(this.context, this.overrideContextSatisfyMode == null ? defaultContextSatisfyMode : this.overrideContextSatisfyMode); case NON_CONTEXTUAL: return true; default: diff --git a/common/src/main/java/me/lucko/luckperms/common/sender/AbstractSender.java b/common/src/main/java/me/lucko/luckperms/common/sender/AbstractSender.java index 97b072493..01eb96988 100644 --- a/common/src/main/java/me/lucko/luckperms/common/sender/AbstractSender.java +++ b/common/src/main/java/me/lucko/luckperms/common/sender/AbstractSender.java @@ -28,6 +28,7 @@ package me.lucko.luckperms.common.sender; import com.google.common.collect.Iterables; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import net.kyori.adventure.text.Component; +import net.kyori.adventure.text.JoinConfiguration; import net.kyori.adventure.text.TextComponent; import net.luckperms.api.util.Tristate; @@ -155,7 +156,7 @@ public final class AbstractSender implements Sender { case 1: return input.get(0); default: - return Component.join(Component.empty(), input); + return Component.join(JoinConfiguration.separator(Component.empty()), input); } }); } diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/AsyncPermissionRegistry.java b/common/src/main/java/me/lucko/luckperms/common/treeview/AsyncPermissionRegistry.java new file mode 100644 index 000000000..68858fc87 --- /dev/null +++ b/common/src/main/java/me/lucko/luckperms/common/treeview/AsyncPermissionRegistry.java @@ -0,0 +1,70 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.treeview; + +import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter; +import me.lucko.luckperms.common.plugin.scheduler.SchedulerTask; + +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.TimeUnit; + +public class AsyncPermissionRegistry extends PermissionRegistry implements AutoCloseable { + + /** A queue of permission strings to be added to the tree */ + private final Queue queue; + /** The tick task */ + private final SchedulerTask task; + + public AsyncPermissionRegistry(SchedulerAdapter scheduler) { + this.queue = new ConcurrentLinkedQueue<>(); + this.task = scheduler.asyncRepeating(this::tick, 1, TimeUnit.SECONDS); + } + + @Override + public void offer(String permission) { + if (permission == null) { + throw new NullPointerException("permission"); + } + this.queue.offer(permission); + } + + private void tick() { + for (String e; (e = this.queue.poll()) != null; ) { + try { + doInsert(e); + } catch (Exception ex) { + // ignore + } + } + } + + @Override + public void close() { + this.task.cancel(); + } + +} diff --git a/common/src/main/java/me/lucko/luckperms/common/treeview/PermissionRegistry.java b/common/src/main/java/me/lucko/luckperms/common/treeview/PermissionRegistry.java index 5e51f0c04..9177bf6f4 100644 --- a/common/src/main/java/me/lucko/luckperms/common/treeview/PermissionRegistry.java +++ b/common/src/main/java/me/lucko/luckperms/common/treeview/PermissionRegistry.java @@ -26,35 +26,20 @@ package me.lucko.luckperms.common.treeview; import com.google.common.base.Splitter; -import me.lucko.luckperms.common.plugin.scheduler.SchedulerAdapter; -import me.lucko.luckperms.common.plugin.scheduler.SchedulerTask; import me.lucko.luckperms.common.util.ImmutableCollectors; import java.util.List; import java.util.Locale; import java.util.Map; -import java.util.Queue; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.TimeUnit; /** * Stores a collection of all permissions known to the platform. */ -public class PermissionRegistry implements AutoCloseable { +public class PermissionRegistry { private static final Splitter DOT_SPLIT = Splitter.on('.').omitEmptyStrings(); /** The root node in the tree */ - private final TreeNode rootNode; - /** A queue of permission strings to be added to the tree */ - private final Queue queue; - /** The tick task */ - private final SchedulerTask task; - - public PermissionRegistry(SchedulerAdapter scheduler) { - this.rootNode = new TreeNode(); - this.queue = new ConcurrentLinkedQueue<>(); - this.task = scheduler.asyncRepeating(this::tick, 1, TimeUnit.SECONDS); - } + private final TreeNode rootNode = new TreeNode(); public TreeNode getRootNode() { return this.rootNode; @@ -66,33 +51,33 @@ public class PermissionRegistry implements AutoCloseable { .collect(ImmutableCollectors.toList()); } + /** + * Offer a permission to the registry (to be potentially inserted asynchronously). + * + * @param permission the permission + */ public void offer(String permission) { + insert(permission); + } + + /** + * Insert a permission into the registry. + * + * @param permission the permission + */ + public void insert(String permission) { if (permission == null) { throw new NullPointerException("permission"); } - this.queue.offer(permission); - } - private void tick() { - for (String e; (e = this.queue.poll()) != null; ) { - insert(e); - } - } - - @Override - public void close() { - this.task.cancel(); - } - - public void insert(String permission) { try { doInsert(permission); } catch (Exception ex) { - ex.printStackTrace(); + // ignore } } - private void doInsert(String permission) { + protected void doInsert(String permission) { permission = permission.toLowerCase(Locale.ROOT); // split the permission up into parts diff --git a/common/src/main/java/me/lucko/luckperms/common/util/DurationFormatter.java b/common/src/main/java/me/lucko/luckperms/common/util/DurationFormatter.java index 23b346877..7073a72e6 100644 --- a/common/src/main/java/me/lucko/luckperms/common/util/DurationFormatter.java +++ b/common/src/main/java/me/lucko/luckperms/common/util/DurationFormatter.java @@ -30,6 +30,7 @@ import net.kyori.adventure.text.Component; import net.kyori.adventure.text.TextComponent; import net.kyori.adventure.text.TranslatableComponent; import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import java.time.Duration; import java.time.temporal.ChronoUnit; @@ -72,7 +73,7 @@ public class DurationFormatter { * @return the formatted string */ public String formatString(Duration duration) { - return PlainComponentSerializer.plain().serialize(TranslationManager.render(format(duration))); + return PlainTextComponentSerializer.plainText().serialize(TranslationManager.render(format(duration))); } /** diff --git a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java index b9dc0e451..335e112de 100644 --- a/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java +++ b/common/src/main/java/me/lucko/luckperms/common/verbose/VerboseListener.java @@ -43,6 +43,7 @@ import me.lucko.luckperms.common.verbose.event.PermissionCheckEvent; import me.lucko.luckperms.common.verbose.event.VerboseEvent; import net.kyori.adventure.text.Component; import net.kyori.adventure.text.ComponentLike; +import net.kyori.adventure.text.JoinConfiguration; import net.kyori.adventure.text.event.HoverEvent; import net.luckperms.api.cacheddata.Result; import net.luckperms.api.node.Node; @@ -222,7 +223,7 @@ public class VerboseListener { } // send the message - HoverEvent hoverEvent = HoverEvent.showText(Component.join(Component.newline(), hover)); + HoverEvent hoverEvent = HoverEvent.showText(Component.join(JoinConfiguration.newlines(), hover)); this.notifiedSender.sendMessage(component.hoverEvent(hoverEvent)); } diff --git a/common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaAccumulatorTest.java b/common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaAccumulatorTest.java new file mode 100644 index 000000000..6e91b7e9b --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaAccumulatorTest.java @@ -0,0 +1,168 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.cacheddata; + +import com.google.common.collect.ListMultimap; +import me.lucko.luckperms.common.cacheddata.metastack.SimpleMetaStackDefinition; +import me.lucko.luckperms.common.cacheddata.metastack.StandardStackElements; +import me.lucko.luckperms.common.cacheddata.result.IntegerResult; +import me.lucko.luckperms.common.cacheddata.result.StringResult; +import me.lucko.luckperms.common.cacheddata.type.MetaAccumulator; +import me.lucko.luckperms.common.node.types.DisplayName; +import me.lucko.luckperms.common.node.types.Meta; +import me.lucko.luckperms.common.node.types.Prefix; +import me.lucko.luckperms.common.node.types.Suffix; +import me.lucko.luckperms.common.node.types.Weight; +import net.luckperms.api.metastacking.DuplicateRemovalFunction; +import net.luckperms.api.metastacking.MetaStackDefinition; +import net.luckperms.api.node.ChatMetaType; +import net.luckperms.api.node.types.MetaNode; +import net.luckperms.api.node.types.PrefixNode; +import net.luckperms.api.node.types.SuffixNode; +import net.luckperms.api.node.types.WeightNode; +import org.junit.jupiter.api.Test; +import org.testcontainers.shaded.com.google.common.collect.ImmutableList; +import org.testcontainers.shaded.com.google.common.collect.ImmutableSet; + +import java.util.SortedMap; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class MetaAccumulatorTest { + + @Test + public void testStates() { + SimpleMetaStackDefinition definition = new SimpleMetaStackDefinition(ImmutableList.of(StandardStackElements.HIGHEST), DuplicateRemovalFunction.RETAIN_ALL, "", "", ""); + MetaAccumulator accumulator = new MetaAccumulator(definition, definition); + + assertThrows(IllegalStateException.class, accumulator::getMeta); + assertThrows(IllegalStateException.class, () -> accumulator.getChatMeta(ChatMetaType.PREFIX)); + assertThrows(IllegalStateException.class, accumulator::getPrefixes); + assertThrows(IllegalStateException.class, accumulator::getSuffixes); + assertThrows(IllegalStateException.class, accumulator::getWeight); + assertThrows(IllegalStateException.class, accumulator::getPrimaryGroup); + assertThrows(IllegalStateException.class, accumulator::getPrefixDefinition); + assertThrows(IllegalStateException.class, accumulator::getSuffixDefinition); + assertThrows(IllegalStateException.class, accumulator::getPrefix); + assertThrows(IllegalStateException.class, accumulator::getSuffix); + + Prefix prefixNode = Prefix.builder("hello", 100).build(); + + accumulator.accumulateNode(prefixNode); + accumulator.complete(); + + assertThrows(IllegalStateException.class, () -> accumulator.accumulateNode(prefixNode)); + + StringResult prefixResult = accumulator.getPrefix(); + assertEquals(prefixNode, prefixResult.node()); + } + + @Test + public void testEmpty() { + SimpleMetaStackDefinition definition = new SimpleMetaStackDefinition(ImmutableList.of(StandardStackElements.HIGHEST), DuplicateRemovalFunction.RETAIN_ALL, "[", "|", "]"); + MetaAccumulator accumulator = new MetaAccumulator(definition, definition); + accumulator.complete(); + + ListMultimap> meta = accumulator.getMeta(); + assertEquals(0, meta.size()); + + SortedMap> prefixes = accumulator.getPrefixes(); + assertEquals(0, prefixes.size()); + + SortedMap> suffixes = accumulator.getSuffixes(); + assertEquals(0, suffixes.size()); + + IntegerResult weight = accumulator.getWeight(); + assertTrue(weight.isNull()); + assertEquals(0, weight.intResult()); + assertNull(weight.node()); + + String primaryGroup = accumulator.getPrimaryGroup(); + assertNull(primaryGroup); + + MetaStackDefinition prefixDefinition = accumulator.getPrefixDefinition(); + assertSame(definition, prefixDefinition); + + MetaStackDefinition suffixDefinition = accumulator.getSuffixDefinition(); + assertSame(definition, suffixDefinition); + + StringResult prefix = accumulator.getPrefix(); + assertNull(prefix.result()); + assertNull(prefix.node()); + + StringResult suffix = accumulator.getSuffix(); + assertNull(suffix.result()); + assertNull(suffix.node()); + } + + @Test + public void testSimple() { + SimpleMetaStackDefinition definition = new SimpleMetaStackDefinition(ImmutableList.of(StandardStackElements.HIGHEST), DuplicateRemovalFunction.RETAIN_ALL, "[", "|", "]"); + MetaAccumulator accumulator = new MetaAccumulator(definition, definition); + + accumulator.accumulateNode(Prefix.builder("b", 90).build()); + accumulator.accumulateNode(Prefix.builder("a", 100).build()); + accumulator.accumulateNode(Prefix.builder("c", 80).build()); + accumulator.accumulateNode(Suffix.builder("foo", 80).build()); + accumulator.accumulateNode(Meta.builder().key("foo").value("bar").build()); + accumulator.accumulateNode(Weight.builder(10).build()); // ignored + accumulator.accumulateNode(DisplayName.builder("hello").build()); + accumulator.accumulateWeight(IntegerResult.of(Weight.builder(5).build())); + accumulator.setPrimaryGroup("member"); + + accumulator.complete(); + + StringResult prefix = accumulator.getPrefix(); + assertEquals("[a]", prefix.result()); + + SortedMap> prefixes = accumulator.getPrefixes(); + assertEquals(ImmutableSet.of(100, 90, 80), prefixes.keySet()); + + StringResult suffix = accumulator.getSuffix(); + assertEquals("[foo]", suffix.result()); + + SortedMap> suffixes = accumulator.getSuffixes(); + assertEquals(ImmutableSet.of(80), suffixes.keySet()); + + ListMultimap> meta = accumulator.getMeta(); + assertEquals(3, meta.size()); + assertEquals(ImmutableSet.of("foo", "weight", "primarygroup"), meta.keySet()); + assertEquals("bar", meta.get("foo").get(0).result()); + assertEquals("5", meta.get("weight").get(0).result()); + assertEquals("member", meta.get("primarygroup").get(0).result()); + + IntegerResult weight = accumulator.getWeight(); + assertEquals(5, weight.intResult()); + + String primaryGroup = accumulator.getPrimaryGroup(); + assertEquals("member", primaryGroup); + } + +} diff --git a/common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaStackAccumulatorTest.java b/common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaStackAccumulatorTest.java new file mode 100644 index 000000000..4b681af90 --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaStackAccumulatorTest.java @@ -0,0 +1,104 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.cacheddata; + +import me.lucko.luckperms.common.cacheddata.metastack.SimpleMetaStackDefinition; +import me.lucko.luckperms.common.cacheddata.metastack.StandardStackElements; +import me.lucko.luckperms.common.cacheddata.result.StringResult; +import me.lucko.luckperms.common.cacheddata.type.MetaStackAccumulator; +import me.lucko.luckperms.common.node.types.Prefix; +import net.luckperms.api.metastacking.DuplicateRemovalFunction; +import net.luckperms.api.node.ChatMetaType; +import net.luckperms.api.node.types.PrefixNode; +import org.junit.jupiter.api.Test; +import org.testcontainers.shaded.com.google.common.collect.ImmutableList; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; + +public class MetaStackAccumulatorTest { + + @Test + public void testEmpty() { + SimpleMetaStackDefinition definition = new SimpleMetaStackDefinition(ImmutableList.of(StandardStackElements.HIGHEST), DuplicateRemovalFunction.RETAIN_ALL, "", "", ""); + MetaStackAccumulator accumulator = new MetaStackAccumulator<>(definition, ChatMetaType.PREFIX); + + StringResult result = accumulator.toResult(); + assertNotNull(result); + assertNull(result.result()); + assertNull(result.node()); + assertNull(result.overriddenResult()); + + String formattedString = accumulator.toFormattedString(); + assertNull(formattedString); + } + + @Test + public void testSingle() { + SimpleMetaStackDefinition definition = new SimpleMetaStackDefinition(ImmutableList.of(StandardStackElements.HIGHEST), DuplicateRemovalFunction.RETAIN_ALL, "[", "|", "]"); + MetaStackAccumulator accumulator = new MetaStackAccumulator<>(definition, ChatMetaType.PREFIX); + + PrefixNode a = Prefix.builder("a", 100).build(); + PrefixNode b = Prefix.builder("b", 90).build(); + PrefixNode c = Prefix.builder("c", 80).build(); + + accumulator.offer(b); + accumulator.offer(a); + accumulator.offer(c); + + StringResult result = accumulator.toResult(); + assertNotNull(result); + assertEquals("[a]", result.result()); + assertEquals(a, result.node()); + assertNull(result.overriddenResult()); + } + + @Test + public void testMultiple() { + SimpleMetaStackDefinition definition = new SimpleMetaStackDefinition(ImmutableList.of(StandardStackElements.LOWEST, StandardStackElements.HIGHEST), DuplicateRemovalFunction.RETAIN_ALL, "[", "|", "]"); + MetaStackAccumulator accumulator = new MetaStackAccumulator<>(definition, ChatMetaType.PREFIX); + + PrefixNode a = Prefix.builder("a", 100).build(); + PrefixNode b = Prefix.builder("b", 90).build(); + PrefixNode c = Prefix.builder("c", 80).build(); + + accumulator.offer(b); + accumulator.offer(a); + accumulator.offer(c); + + StringResult result = accumulator.toResult(); + assertNotNull(result); + assertEquals("[c|a]", result.result()); + assertEquals(c, result.node()); + + StringResult overriddenResult = result.overriddenResult(); + assertNotNull(overriddenResult); + assertEquals(a, overriddenResult.node()); + assertNull(overriddenResult.overriddenResult()); + } + +} diff --git a/common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaStackElementTest.java b/common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaStackElementTest.java new file mode 100644 index 000000000..2a73395e5 --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaStackElementTest.java @@ -0,0 +1,243 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.cacheddata; + +import me.lucko.luckperms.common.cacheddata.metastack.StandardStackElements; +import me.lucko.luckperms.common.model.HolderType; +import me.lucko.luckperms.common.model.InheritanceOrigin; +import me.lucko.luckperms.common.model.PermissionHolderIdentifier; +import me.lucko.luckperms.common.model.Track; +import me.lucko.luckperms.common.model.manager.track.StandardTrackManager; +import me.lucko.luckperms.common.model.manager.track.TrackManager; +import me.lucko.luckperms.common.node.types.Prefix; +import me.lucko.luckperms.common.plugin.LuckPermsPlugin; +import net.luckperms.api.metastacking.MetaStackElement; +import net.luckperms.api.model.data.DataType; +import net.luckperms.api.node.ChatMetaType; +import net.luckperms.api.node.metadata.types.InheritanceOriginMetadata; +import org.junit.jupiter.api.Test; +import org.testcontainers.shaded.com.google.common.collect.ImmutableList; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class MetaStackElementTest { + + @Test + public void testHighest() { + MetaStackElement highest = StandardStackElements.HIGHEST; + assertTrue(highest.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).build(), // node + null // current + )); + assertFalse(highest.shouldAccumulate( + ChatMetaType.SUFFIX, + Prefix.builder("foo", 100).build(), // node + null // current + )); + assertTrue(highest.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).build(), // node + Prefix.builder("bar", 50).build()) // current + ); + assertFalse(highest.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 50).build(), // node + Prefix.builder("bar", 100).build()) // current + ); + } + + @Test + public void testLowest() { + MetaStackElement lowest = StandardStackElements.LOWEST; + assertTrue(lowest.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).build(), // node + null // current + )); + assertFalse(lowest.shouldAccumulate( + ChatMetaType.SUFFIX, + Prefix.builder("foo", 100).build(), // node + null // current + )); + assertTrue(lowest.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 50).build(), // node + Prefix.builder("bar", 100).build()) // current + ); + assertFalse(lowest.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).build(), // node + Prefix.builder("bar", 50).build()) // current + ); + } + + @Test + public void testHighestOwn() { + InheritanceOrigin userOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.USER, ""), DataType.NORMAL); + InheritanceOrigin groupOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.GROUP, ""), DataType.NORMAL); + + MetaStackElement highestOwn = StandardStackElements.HIGHEST_OWN; + assertTrue(highestOwn.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, userOrigin).build(), // node + null // current + )); + assertFalse(highestOwn.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, groupOrigin).build(), // node + null // current + )); + } + + @Test + public void testHighestInherited() { + InheritanceOrigin userOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.USER, ""), DataType.NORMAL); + InheritanceOrigin groupOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.GROUP, ""), DataType.NORMAL); + + MetaStackElement highestInherited = StandardStackElements.HIGHEST_INHERITED; + assertTrue(highestInherited.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, groupOrigin).build(), // node + null // current + )); + assertFalse(highestInherited.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, userOrigin).build(), // node + null // current + )); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Test + public void testHighestFromGroupOnTrack() { + LuckPermsPlugin plugin = mock(LuckPermsPlugin.class); + TrackManager trackManager = new StandardTrackManager(plugin); + when(plugin.getTrackManager()).thenReturn((TrackManager) trackManager); + + Track track = trackManager.getOrMake("test"); + track.setGroups(ImmutableList.of("foo", "bar")); + + InheritanceOrigin fooOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.GROUP, "foo"), DataType.NORMAL); + InheritanceOrigin bazOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.GROUP, "baz"), DataType.NORMAL); + + MetaStackElement highestFromGroupOnTrack = StandardStackElements.highestFromGroupOnTrack(plugin, "test"); + assertTrue(highestFromGroupOnTrack.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, fooOrigin).build(), // node + null // current + )); + assertFalse(highestFromGroupOnTrack.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, bazOrigin).build(), // node + null // current + )); + } + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Test + public void testHighestNotFromGroupOnTrack() { + LuckPermsPlugin plugin = mock(LuckPermsPlugin.class); + TrackManager trackManager = new StandardTrackManager(plugin); + when(plugin.getTrackManager()).thenReturn((TrackManager) trackManager); + + Track track = trackManager.getOrMake("test"); + track.setGroups(ImmutableList.of("foo", "bar")); + + InheritanceOrigin fooOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.GROUP, "foo"), DataType.NORMAL); + InheritanceOrigin bazOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.GROUP, "baz"), DataType.NORMAL); + InheritanceOrigin userOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.USER, "foo"), DataType.NORMAL); + + MetaStackElement highestNotFromGroupOnTrack = StandardStackElements.highestNotFromGroupOnTrack(plugin, "test"); + assertTrue(highestNotFromGroupOnTrack.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, userOrigin).build(), // node + null // current + )); + assertTrue(highestNotFromGroupOnTrack.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, bazOrigin).build(), // node + null // current + )); + assertFalse(highestNotFromGroupOnTrack.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, fooOrigin).build(), // node + null // current + )); + } + + @Test + public void testHighestFromGroup() { + InheritanceOrigin fooOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.GROUP, "foo"), DataType.NORMAL); + InheritanceOrigin bazOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.GROUP, "baz"), DataType.NORMAL); + InheritanceOrigin userOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.USER, "foo"), DataType.NORMAL); + + MetaStackElement highestFromGroup = StandardStackElements.highestFromGroup("foo"); + assertTrue(highestFromGroup.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, fooOrigin).build(), // node + null // current + )); + assertFalse(highestFromGroup.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, bazOrigin).build(), // node + null // current + )); + assertFalse(highestFromGroup.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, userOrigin).build(), // node + null // current + )); + } + + @Test + public void testHighestNotFromGroup() { + InheritanceOrigin fooOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.GROUP, "foo"), DataType.NORMAL); + InheritanceOrigin bazOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.GROUP, "baz"), DataType.NORMAL); + InheritanceOrigin userOrigin = new InheritanceOrigin(new PermissionHolderIdentifier(HolderType.USER, "foo"), DataType.NORMAL); + + MetaStackElement highestNotFromGroup = StandardStackElements.highestNotFromGroup("foo"); + assertTrue(highestNotFromGroup.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, userOrigin).build(), // node + null // current + )); + assertTrue(highestNotFromGroup.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, bazOrigin).build(), // node + null // current + )); + assertFalse(highestNotFromGroup.shouldAccumulate( + ChatMetaType.PREFIX, + Prefix.builder("foo", 100).withMetadata(InheritanceOriginMetadata.KEY, fooOrigin).build(), // node + null // current + )); + } + +} diff --git a/common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaValueSelectorTest.java b/common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaValueSelectorTest.java new file mode 100644 index 000000000..c66f277ab --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/cacheddata/MetaValueSelectorTest.java @@ -0,0 +1,81 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.cacheddata; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import me.lucko.luckperms.common.cacheddata.result.StringResult; +import me.lucko.luckperms.common.cacheddata.type.SimpleMetaValueSelector; +import me.lucko.luckperms.common.cacheddata.type.SimpleMetaValueSelector.Strategy; +import net.luckperms.api.cacheddata.Result; +import net.luckperms.api.node.types.MetaNode; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertThrows; + +public class MetaValueSelectorTest { + + @Test + public void testStrategies() { + Map strategies = ImmutableMap.of( + "foo", Strategy.HIGHEST_NUMBER, + "bar", Strategy.LOWEST_NUMBER + ); + SimpleMetaValueSelector selector = new SimpleMetaValueSelector(strategies, Strategy.INHERITANCE); + + // empty + assertThrows(IllegalArgumentException.class, () -> selector.selectValue("hello", ImmutableList.of())); + + Result foo = StringResult.of("foo"); + + // single value + Result value = selector.selectValue("abc", ImmutableList.of(foo)); + assertSame(foo, value); + + // fallback to default when values are not numbers + value = selector.selectValue("foo", ImmutableList.of(foo)); + assertSame(foo, value); + + Result one = StringResult.of("1"); + Result two = StringResult.of("2"); + Result three = StringResult.of("3"); + ImmutableList> values = ImmutableList.of(two, one, three); + + // first value + assertSame(two, selector.selectValue("abc", values)); + + // highest value + assertSame(three, selector.selectValue("foo", values)); + + // lowest value + assertSame(one, selector.selectValue("bar", values)); + + } + +} diff --git a/common/src/test/java/me/lucko/luckperms/common/cacheddata/UsageTrackedTest.java b/common/src/test/java/me/lucko/luckperms/common/cacheddata/UsageTrackedTest.java new file mode 100644 index 000000000..74812b961 --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/cacheddata/UsageTrackedTest.java @@ -0,0 +1,54 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.cacheddata; + +import org.junit.jupiter.api.Test; + +import java.util.concurrent.TimeUnit; + +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class UsageTrackedTest { + + @Test + public void testUsedRecently() { + TestUsageTracked usageTracked = new TestUsageTracked(); + assertTrue(usageTracked.usedInTheLast(1, TimeUnit.MINUTES)); + usageTracked.recordUsage(); + assertTrue(usageTracked.usedInTheLast(1, TimeUnit.MINUTES)); + + usageTracked.setLastUsed(System.currentTimeMillis() - TimeUnit.HOURS.toMillis(1)); + assertFalse(usageTracked.usedInTheLast(1, TimeUnit.MINUTES)); + } + + static final class TestUsageTracked extends UsageTracked { + void setLastUsed(long lastUsed) { + this.lastUsed = lastUsed; + } + } + +} diff --git a/common/src/test/java/me/lucko/luckperms/common/filter/FilterMongoTest.java b/common/src/test/java/me/lucko/luckperms/common/filter/FilterMongoTest.java index bb68bb20f..913bb0f2f 100644 --- a/common/src/test/java/me/lucko/luckperms/common/filter/FilterMongoTest.java +++ b/common/src/test/java/me/lucko/luckperms/common/filter/FilterMongoTest.java @@ -78,6 +78,21 @@ public class FilterMongoTest { ), // {"$or": [{"foo": "hello"}, {"bar": "world"}]} "{\"$or\": [{\"foo\": \"hello\"}, {\"bar\": \"world\"}]}" + ), + Arguments.of( + FilterList.or( + TestField.FOO.isEqualTo("hello", ConstraintFactory.STRINGS), + TestField.BAR.isNotEqualTo("world", ConstraintFactory.STRINGS), + TestField.BAZ.isSimilarTo("abc%xyz", ConstraintFactory.STRINGS), + TestField.BAZ.isNotSimilarTo("a_c", ConstraintFactory.STRINGS) + ), + // {"$or": [ + // {"foo": "hello"}, + // {"bar": {"$ne": "world"}}, + // {"baz": {"$regularExpression": {"pattern": "abc.*xyz", "options": "i"}}}, + // {"baz": {"$not": {"$regularExpression": {"pattern": "a.c", "options": "i"}}}} + // ]} + "{\"$or\": [{\"foo\": \"hello\"}, {\"bar\": {\"$ne\": \"world\"}}, {\"baz\": {\"$regularExpression\": {\"pattern\": \"abc.*xyz\", \"options\": \"i\"}}}, {\"baz\": {\"$not\": {\"$regularExpression\": {\"pattern\": \"a.c\", \"options\": \"i\"}}}}]}" ) ); } @@ -94,7 +109,7 @@ public class FilterMongoTest { } private enum TestField implements FilterField { - FOO, BAR; + FOO, BAR, BAZ; @Override public String getValue(Object object) { diff --git a/common/src/test/java/me/lucko/luckperms/common/filter/FilterSqlTest.java b/common/src/test/java/me/lucko/luckperms/common/filter/FilterSqlTest.java index 2870128b3..826c8908b 100644 --- a/common/src/test/java/me/lucko/luckperms/common/filter/FilterSqlTest.java +++ b/common/src/test/java/me/lucko/luckperms/common/filter/FilterSqlTest.java @@ -73,6 +73,16 @@ public class FilterSqlTest { ), " WHERE foo = hello OR bar = world", " WHERE foo = ? OR bar = ?" + ), + Arguments.of( + FilterList.or( + TestField.FOO.isEqualTo("hello", ConstraintFactory.STRINGS), + TestField.BAR.isNotEqualTo("world", ConstraintFactory.STRINGS), + TestField.BAZ.isSimilarTo("abc%xyz", ConstraintFactory.STRINGS), + TestField.BAZ.isNotSimilarTo("a_c", ConstraintFactory.STRINGS) + ), + " WHERE foo = hello OR bar != world OR baz LIKE abc%xyz OR baz NOT LIKE a_c", + " WHERE foo = ? OR bar != ? OR baz LIKE ? OR baz NOT LIKE ?" ) ); } @@ -80,7 +90,7 @@ public class FilterSqlTest { @ParameterizedTest(name = "[{index}] {0}") @MethodSource public void testQueries(FilterList filters, String expectedSql, String expectedSqlParams) { - TestFilterMongoBuilder sqlBuilder = new TestFilterMongoBuilder(); + FilterSqlBuilder sqlBuilder = new TestFilterSqlBuilder(); sqlBuilder.visit(filters); System.out.println(sqlBuilder.builder().toReadableString()); @@ -91,7 +101,7 @@ public class FilterSqlTest { } private enum TestField implements FilterField { - FOO, BAR; + FOO, BAR, BAZ; @Override public String getValue(Object object) { @@ -104,7 +114,7 @@ public class FilterSqlTest { } } - private static final class TestFilterMongoBuilder extends FilterSqlBuilder { + private static final class TestFilterSqlBuilder extends FilterSqlBuilder { @Override public void visitFieldName(FilterField field) { diff --git a/common/src/test/java/me/lucko/luckperms/common/node/NodeCommandFactoryTest.java b/common/src/test/java/me/lucko/luckperms/common/node/NodeCommandFactoryTest.java new file mode 100644 index 000000000..5cc405bcb --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/node/NodeCommandFactoryTest.java @@ -0,0 +1,71 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.node; + +import me.lucko.luckperms.common.model.HolderType; +import me.lucko.luckperms.common.node.factory.NodeCommandFactory; +import me.lucko.luckperms.common.node.types.Inheritance; +import me.lucko.luckperms.common.node.types.Meta; +import me.lucko.luckperms.common.node.types.Permission; +import me.lucko.luckperms.common.node.types.Prefix; +import net.luckperms.api.node.Node; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.concurrent.TimeUnit; +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class NodeCommandFactoryTest { + + private static Stream testUndoCommand() { + return Stream.of( + Arguments.of("group test permission unset test", Permission.builder().permission("test").build(), HolderType.GROUP, false), + Arguments.of("user test permission unset test", Permission.builder().permission("test").build(), HolderType.USER, false), + Arguments.of("user test permission unsettemp test", Permission.builder().permission("test").expiry(1, TimeUnit.HOURS).build(), HolderType.USER, false), + Arguments.of("user test permission unset test server=foo world=bar", Permission.builder().permission("test").withContext("server", "foo").withContext("world", "bar").build(), HolderType.USER, false), + Arguments.of("user test permission unset test global", Permission.builder().permission("test").build(), HolderType.USER, true), + Arguments.of("user test parent remove test", Inheritance.builder().group("test").build(), HolderType.USER, false), + Arguments.of("user test parent removetemp test", Inheritance.builder().group("test").expiry(1, TimeUnit.HOURS).build(), HolderType.USER, false), + Arguments.of("user test meta removeprefix 100 test", Prefix.builder().priority(100).prefix("test").build(), HolderType.USER, false), + Arguments.of("user test meta removetempprefix 100 test", Prefix.builder().priority(100).prefix("test").expiry(1, TimeUnit.HOURS).build(), HolderType.USER, false), + Arguments.of("user test meta removeprefix 100 \"hello world\"", Prefix.builder().priority(100).prefix("hello world").build(), HolderType.USER, false), + Arguments.of("user test meta unset foo", Meta.builder().key("foo").value("bar").build(), HolderType.USER, false), + Arguments.of("user test meta unsettemp foo", Meta.builder().key("foo").value("bar").expiry(1, TimeUnit.HOURS).build(), HolderType.USER, false) + ); + } + + @ParameterizedTest(name = "[{index}] {0}") + @MethodSource + public void testUndoCommand(String expected, Node node, HolderType holderType, boolean explicitGlobalContext) { + String result = NodeCommandFactory.undoCommand(node, "test", holderType, explicitGlobalContext); + assertEquals(expected, result); + + } + +} diff --git a/common/src/test/java/me/lucko/luckperms/common/node/NodeMatcherTest.java b/common/src/test/java/me/lucko/luckperms/common/node/NodeMatcherTest.java new file mode 100644 index 000000000..51c27eb18 --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/node/NodeMatcherTest.java @@ -0,0 +1,111 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.node; + +import me.lucko.luckperms.common.filter.Comparison; +import me.lucko.luckperms.common.filter.Constraint; +import me.lucko.luckperms.common.node.matcher.ConstraintNodeMatcher; +import me.lucko.luckperms.common.node.matcher.StandardNodeMatchers; +import me.lucko.luckperms.common.node.types.Permission; +import net.luckperms.api.node.Node; +import net.luckperms.api.node.NodeType; +import net.luckperms.api.node.types.MetaNode; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class NodeMatcherTest { + + @Test + public void testKey() { + ConstraintNodeMatcher matcher = StandardNodeMatchers.key("foo"); + Constraint constraint = matcher.getConstraint(); + + assertEquals(Comparison.EQUAL, constraint.comparison()); + assertEquals("foo", constraint.value()); + + ConstraintNodeMatcher typedMatcher = StandardNodeMatchers.key(Permission.builder().permission("foo").build()); + Constraint typedConstraint = typedMatcher.getConstraint(); + + assertEquals(Comparison.EQUAL, typedConstraint.comparison()); + assertEquals("foo", typedConstraint.value()); + } + + @ParameterizedTest + @CsvSource({ + "foo, foo%", + }) + public void testKeyStartsWith(String value, String expectedConstraint) { + ConstraintNodeMatcher matcher = StandardNodeMatchers.keyStartsWith(value); + Constraint constraint = matcher.getConstraint(); + + assertEquals(Comparison.SIMILAR, constraint.comparison()); + assertEquals(expectedConstraint, constraint.value()); + } + + @ParameterizedTest + @CsvSource({ + "foo, meta.foo.%", + }) + public void testMetaKey(String value, String expectedConstraint) { + ConstraintNodeMatcher matcher = StandardNodeMatchers.metaKey(value); + Constraint constraint = matcher.getConstraint(); + + assertEquals(Comparison.SIMILAR, constraint.comparison()); + assertEquals(expectedConstraint, constraint.value()); + } + + + private static Stream testType() { + return Stream.of( + Arguments.of(NodeType.REGEX_PERMISSION, "r=%"), + Arguments.of(NodeType.INHERITANCE, "group.%"), + Arguments.of(NodeType.PREFIX, "prefix.%.%"), + Arguments.of(NodeType.SUFFIX, "suffix.%.%"), + Arguments.of(NodeType.META, "meta.%.%"), + Arguments.of(NodeType.WEIGHT, "weight.%"), + Arguments.of(NodeType.DISPLAY_NAME, "displayname.%") + ); + } + + @ParameterizedTest + @MethodSource + public void testType(NodeType type, String expectedValue) { + ConstraintNodeMatcher matcher = StandardNodeMatchers.type(type); + Constraint constraint = matcher.getConstraint(); + + assertEquals(Comparison.SIMILAR, constraint.comparison()); + assertEquals(expectedValue, constraint.value()); + + } + +} diff --git a/common/src/test/java/me/lucko/luckperms/common/node/NodeParseTest.java b/common/src/test/java/me/lucko/luckperms/common/node/NodeParseTest.java index c4bab713c..bf392abf4 100644 --- a/common/src/test/java/me/lucko/luckperms/common/node/NodeParseTest.java +++ b/common/src/test/java/me/lucko/luckperms/common/node/NodeParseTest.java @@ -139,7 +139,9 @@ public class NodeParseTest { "prefix.0.hello, 0, hello", "prefix.100.hello world, 100, hello world", "prefix.100.HELLO world &123, 100, HELLO world &123", - "prefix.100., 100, ''" + "prefix.100., 100, ''", + "prefix.100.hello\\.world, 100, hello.world", + "prefix.100.hello.world, 100, hello.world", }) public void testPrefix(String key, int expectedPriority, String expectedValue) { Prefix.Builder builder = Prefix.parse(key); @@ -157,7 +159,8 @@ public class NodeParseTest { "prefix.", "prefix.hello", "prefix.100", - "prefix.hello.hello" + "prefix.hello.hello", + "suffix.100.hello" }) public void testPrefixFail(String key) { Prefix.Builder builder = Prefix.parse(key); @@ -171,7 +174,9 @@ public class NodeParseTest { "suffix.0.hello, 0, hello", "suffix.100.hello world, 100, hello world", "suffix.100.HELLO world &123, 100, HELLO world &123", - "suffix.100., 100, ''" + "suffix.100., 100, ''", + "suffix.100.hello\\.world, 100, hello.world", + "suffix.100.hello.world, 100, hello.world", }) public void testSuffix(String key, int expectedPriority, String expectedValue) { Suffix.Builder builder = Suffix.parse(key); @@ -189,7 +194,8 @@ public class NodeParseTest { "suffix.", "suffix.hello", "suffix.100", - "suffix.hello.hello" + "suffix.hello.hello", + "prefix.100.hello" }) public void testSuffixFail(String key) { Suffix.Builder builder = Suffix.parse(key); @@ -200,7 +206,10 @@ public class NodeParseTest { @CsvSource({ "meta.k.v, k, v", "meta.hello.world, hello, world", - "meta.hello., hello, ''" + "meta.hello., hello, ''", + "meta.a\\.b.hel\\.lo, a.b, hel.lo", + "meta.a\\\\.b.hel\\.lo, a\\.b, hel.lo", + "meta.a.b.c, a, b.c" }) public void testMeta(String key, String expectedKey, String expectedValue) { Meta.Builder builder = Meta.parse(key); diff --git a/common/src/test/java/me/lucko/luckperms/common/node/NodeTest.java b/common/src/test/java/me/lucko/luckperms/common/node/NodeTest.java index 94a84b307..203c9e73c 100644 --- a/common/src/test/java/me/lucko/luckperms/common/node/NodeTest.java +++ b/common/src/test/java/me/lucko/luckperms/common/node/NodeTest.java @@ -26,7 +26,10 @@ package me.lucko.luckperms.common.node; import me.lucko.luckperms.common.context.ImmutableContextSetImpl; +import me.lucko.luckperms.common.node.types.Meta; import me.lucko.luckperms.common.node.types.Permission; +import me.lucko.luckperms.common.node.types.Prefix; +import me.lucko.luckperms.common.node.types.Suffix; import net.luckperms.api.context.ImmutableContextSet; import net.luckperms.api.node.Node; import net.luckperms.api.node.metadata.NodeMetadataKey; @@ -60,6 +63,18 @@ public class NodeTest { assertFalse(node.hasExpiry()); } + @Test + public void testEscaping() { + Node node = Meta.builder("hel.lo", "wo.rld").build(); + assertEquals("meta.hel\\.lo.wo\\.rld", node.getKey()); + + node = Prefix.builder("hel.lo", 100).build(); + assertEquals("prefix.100.hel\\.lo", node.getKey()); + + node = Suffix.builder("hel.lo", 100).build(); + assertEquals("suffix.100.hel\\.lo", node.getKey()); + } + @Test public void testExpiry() { Instant instant = Instant.now() diff --git a/common/src/test/java/me/lucko/luckperms/common/node/ShorthandParserTest.java b/common/src/test/java/me/lucko/luckperms/common/node/ShorthandParserTest.java index 23b1c9968..5503fae81 100644 --- a/common/src/test/java/me/lucko/luckperms/common/node/ShorthandParserTest.java +++ b/common/src/test/java/me/lucko/luckperms/common/node/ShorthandParserTest.java @@ -31,19 +31,24 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import org.junit.jupiter.params.provider.ValueSource; import java.util.stream.Stream; +import static org.junit.jupiter.api.Assertions.assertTrue; + public class ShorthandParserTest { private static Stream testParse() { return Stream.of( // numeric range Arguments.of("{2-4}", new String[]{"2", "3", "4"}), + Arguments.of("{4-2}", new String[]{"2", "3", "4"}), // character range Arguments.of("{a-d}", new String[]{"a", "b", "c", "d"}), Arguments.of("{A-D}", new String[]{"A", "B", "C", "D"}), + Arguments.of("{D-A}", new String[]{"A", "B", "C", "D"}), // list Arguments.of("{aa,bb,cc}", new String[]{"aa", "bb", "cc"}), @@ -68,4 +73,15 @@ public class ShorthandParserTest { Assertions.assertEquals(ImmutableSet.copyOf(expected), ShorthandParser.expandShorthand(shorthand)); } + @ParameterizedTest + @ValueSource(strings = { + "{1-1000}", + "{1000-1}", + "{!-က}", + "{က-!}", + }) + public void testTooManyElements(String shorthand) { + assertTrue(ShorthandParser.expandShorthand(shorthand).size() <= 1); + } + } diff --git a/common/src/test/java/me/lucko/luckperms/common/query/DataSelectorTest.java b/common/src/test/java/me/lucko/luckperms/common/query/DataSelectorTest.java new file mode 100644 index 000000000..e3e3c7a85 --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/query/DataSelectorTest.java @@ -0,0 +1,84 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.query; + +import me.lucko.luckperms.common.model.HolderType; +import me.lucko.luckperms.common.model.PermissionHolderIdentifier; +import net.luckperms.api.model.data.DataType; +import net.luckperms.api.query.QueryMode; +import net.luckperms.api.query.QueryOptions; +import net.luckperms.api.query.dataorder.DataQueryOrder; +import net.luckperms.api.query.dataorder.DataQueryOrderFunction; +import net.luckperms.api.query.dataorder.DataTypeFilter; +import net.luckperms.api.query.dataorder.DataTypeFilterFunction; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; + +public class DataSelectorTest { + private static final PermissionHolderIdentifier IDENTIFIER = new PermissionHolderIdentifier(HolderType.USER, "Notch"); + + @Test + public void testDefault() { + DataType[] types = DataSelector.selectOrder(QueryOptionsImpl.DEFAULT_CONTEXTUAL, IDENTIFIER); + assertArrayEquals(new DataType[]{DataType.TRANSIENT, DataType.NORMAL}, types); + } + + @Test + public void testOrdering() { + QueryOptions transientFirst = new QueryOptionsBuilderImpl(QueryMode.CONTEXTUAL) + .option(DataQueryOrderFunction.KEY, DataQueryOrderFunction.always(DataQueryOrder.TRANSIENT_FIRST)) + .build(); + + QueryOptions transientLast = new QueryOptionsBuilderImpl(QueryMode.CONTEXTUAL) + .option(DataQueryOrderFunction.KEY, DataQueryOrderFunction.always(DataQueryOrder.TRANSIENT_LAST)) + .build(); + + DataType[] types = DataSelector.selectOrder(transientFirst, IDENTIFIER); + assertArrayEquals(new DataType[]{DataType.TRANSIENT, DataType.NORMAL}, types); + + types = DataSelector.selectOrder(transientLast, IDENTIFIER); + assertArrayEquals(new DataType[]{DataType.NORMAL, DataType.TRANSIENT}, types); + } + + @Test + public void testSelection() { + QueryOptions normalOnly = new QueryOptionsBuilderImpl(QueryMode.CONTEXTUAL) + .option(DataTypeFilterFunction.KEY, DataTypeFilterFunction.always(DataTypeFilter.NORMAL_ONLY)) + .build(); + + QueryOptions transientOnly = new QueryOptionsBuilderImpl(QueryMode.CONTEXTUAL) + .option(DataTypeFilterFunction.KEY, DataTypeFilterFunction.always(DataTypeFilter.TRANSIENT_ONLY)) + .build(); + + DataType[] types = DataSelector.selectOrder(normalOnly, IDENTIFIER); + assertArrayEquals(new DataType[]{DataType.NORMAL}, types); + + types = DataSelector.selectOrder(transientOnly, IDENTIFIER); + assertArrayEquals(new DataType[]{DataType.TRANSIENT}, types); + } + +} diff --git a/common/src/test/java/me/lucko/luckperms/common/query/QueryOptionsTest.java b/common/src/test/java/me/lucko/luckperms/common/query/QueryOptionsTest.java new file mode 100644 index 000000000..4e79cd856 --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/query/QueryOptionsTest.java @@ -0,0 +1,62 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.query; + +import net.luckperms.api.query.Flag; +import net.luckperms.api.query.QueryMode; +import net.luckperms.api.query.QueryOptions; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +public class QueryOptionsTest { + + @Test + public void testFlags() { + QueryOptions options = new QueryOptionsBuilderImpl(QueryMode.CONTEXTUAL).build(); + assertSame(QueryOptionsImpl.DEFAULT_CONTEXTUAL, options); + assertEquals(Flag.values().length, options.flags().size()); + + for (Flag flag : Flag.values()) { + assertTrue(options.flag(flag)); + } + + options = new QueryOptionsBuilderImpl(QueryMode.CONTEXTUAL).flag(Flag.APPLY_INHERITANCE_NODES_WITHOUT_WORLD_CONTEXT, false).build(); + assertEquals(Flag.values().length - 1, options.flags().size()); + + for (Flag flag : Flag.values()) { + if (flag == Flag.APPLY_INHERITANCE_NODES_WITHOUT_WORLD_CONTEXT) { + assertFalse(options.flag(flag)); + } else { + assertTrue(options.flag(flag)); + } + } + } + +} diff --git a/common/src/test/java/me/lucko/luckperms/common/treeview/PermissionRegistryTest.java b/common/src/test/java/me/lucko/luckperms/common/treeview/PermissionRegistryTest.java new file mode 100644 index 000000000..bd228181c --- /dev/null +++ b/common/src/test/java/me/lucko/luckperms/common/treeview/PermissionRegistryTest.java @@ -0,0 +1,81 @@ +/* + * This file is part of LuckPerms, licensed under the MIT License. + * + * Copyright (c) lucko (Luck) + * Copyright (c) contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.lucko.luckperms.common.treeview; + +import com.google.gson.JsonObject; +import org.junit.jupiter.api.Test; +import org.testcontainers.shaded.com.google.common.collect.ImmutableSet; + +import java.util.List; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; + +public class PermissionRegistryTest { + + @Test + public void testEmpty() { + PermissionRegistry registry = new PermissionRegistry(); + assertEquals(0, registry.rootAsList().size()); + + TreeNode node = registry.getRootNode(); + assertFalse(node.getChildren().isPresent()); + assertEquals(0, node.getChildrenSize()); + } + + @Test + public void testBasic() { + PermissionRegistry registry = new PermissionRegistry(); + registry.insert("minecraft.command.give"); + registry.insert("minecraft.command.time"); + registry.insert("worldedit.clipboard.copy"); + registry.insert("worldedit.clipboard.paste"); + + List permissions = registry.rootAsList(); + assertEquals( + ImmutableSet.of( + "minecraft", "minecraft.command", "minecraft.command.give", "minecraft.command.time", + "worldedit", "worldedit.clipboard", "worldedit.clipboard.copy", "worldedit.clipboard.paste" + ), + ImmutableSet.copyOf(permissions) + ); + } + + @Test + public void testExport() { + PermissionRegistry registry = new PermissionRegistry(); + registry.insert("minecraft.command.give"); + registry.insert("minecraft.command.time"); + registry.insert("worldedit.clipboard.copy"); + registry.insert("worldedit.clipboard.paste"); + + ImmutableTreeNode immutableNode = registry.getRootNode().makeImmutableCopy(); + JsonObject json = immutableNode.toJson(""); + // {"minecraft":{"minecraft.command":{"minecraft.command.give":{},"minecraft.command.time":{}}},"worldedit":{"worldedit.clipboard":{"worldedit.clipboard.copy":{},"worldedit.clipboard.paste":{}}}} + assertEquals("{\"minecraft\":{\"minecraft.command\":{\"minecraft.command.give\":{},\"minecraft.command.time\":{}}},\"worldedit\":{\"worldedit.clipboard\":{\"worldedit.clipboard.copy\":{},\"worldedit.clipboard.paste\":{}}}}", json.toString()); + } + +} diff --git a/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java b/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java index 7834a4a54..40f422203 100644 --- a/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java +++ b/fabric/src/main/java/me/lucko/luckperms/fabric/LPFabricPlugin.java @@ -51,7 +51,7 @@ import me.lucko.luckperms.fabric.messaging.FabricMessagingFactory; import net.fabricmc.fabric.api.event.lifecycle.v1.ServerLifecycleEvents; import net.fabricmc.loader.api.ModContainer; import net.kyori.adventure.text.Component; -import net.kyori.adventure.text.serializer.plain.PlainComponentSerializer; +import net.kyori.adventure.text.serializer.plain.PlainTextComponentSerializer; import net.luckperms.api.LuckPerms; import net.luckperms.api.query.QueryOptions; import net.minecraft.server.MinecraftServer; @@ -244,7 +244,7 @@ public class LPFabricPlugin extends AbstractLuckPermsPlugin { .orElseGet(() -> new DummyConsoleSender(this) { @Override public void sendMessage(Component message) { - LPFabricPlugin.this.bootstrap.getPluginLogger().info(PlainComponentSerializer.plain().serialize(TranslationManager.render(message))); + LPFabricPlugin.this.bootstrap.getPluginLogger().info(PlainTextComponentSerializer.plainText().serialize(TranslationManager.render(message))); } }); } diff --git a/forge/src/main/java/me/lucko/luckperms/forge/messaging/PluginMessageMessenger.java b/forge/src/main/java/me/lucko/luckperms/forge/messaging/PluginMessageMessenger.java index 2d5abab89..ee5621488 100644 --- a/forge/src/main/java/me/lucko/luckperms/forge/messaging/PluginMessageMessenger.java +++ b/forge/src/main/java/me/lucko/luckperms/forge/messaging/PluginMessageMessenger.java @@ -92,6 +92,7 @@ public class PluginMessageMessenger extends AbstractPluginMessageMessenger imple taskRef.set(task); } + @SuppressWarnings("EmptyMethod") public static void registerChannel() { // do nothing - the channels are registered in the static initializer, we just // need to make sure that is called (which it will be if this method runs) diff --git a/neoforge/src/main/java/me/lucko/luckperms/neoforge/messaging/PluginMessageMessenger.java b/neoforge/src/main/java/me/lucko/luckperms/neoforge/messaging/PluginMessageMessenger.java index 49fb23321..f6586cc5f 100644 --- a/neoforge/src/main/java/me/lucko/luckperms/neoforge/messaging/PluginMessageMessenger.java +++ b/neoforge/src/main/java/me/lucko/luckperms/neoforge/messaging/PluginMessageMessenger.java @@ -99,6 +99,7 @@ public class PluginMessageMessenger extends AbstractPluginMessageMessenger imple taskRef.set(task); } + @SuppressWarnings("EmptyMethod") public static void registerChannel() { // do nothing - the channels are registered in the static initializer, we just // need to make sure that is called (which it will be if this method runs) diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubject.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubject.java index 98fd97ab5..a5148a295 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubject.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubject.java @@ -27,7 +27,6 @@ package me.lucko.luckperms.sponge.service.model.calculated; import com.google.common.collect.ImmutableList; import me.lucko.luckperms.common.cacheddata.type.MetaAccumulator; -import me.lucko.luckperms.common.cacheddata.type.MetaCache; import me.lucko.luckperms.common.graph.TraversalAlgorithm; import me.lucko.luckperms.common.query.QueryOptionsImpl; import me.lucko.luckperms.common.verbose.event.CheckOrigin; @@ -203,7 +202,7 @@ public abstract class CalculatedSubject implements LPSubject { @Override public Optional getOption(ImmutableContextSet contexts, String key) { - return Optional.ofNullable(((MetaCache) this.cachedData.getMetaData(QueryOptionsImpl.DEFAULT_CONTEXTUAL.toBuilder().context(contexts).build())).getMetaValue(key, CheckOrigin.PLATFORM_API).result()); + return Optional.ofNullable(this.cachedData.getMetaData(QueryOptionsImpl.DEFAULT_CONTEXTUAL.toBuilder().context(contexts).build()).getMetaValue(key, CheckOrigin.PLATFORM_API).result()); } @Override diff --git a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectCachedDataManager.java b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectCachedDataManager.java index 9509ea9a3..8cb13ccc4 100644 --- a/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectCachedDataManager.java +++ b/sponge/src/main/java/me/lucko/luckperms/sponge/service/model/calculated/CalculatedSubjectCachedDataManager.java @@ -28,6 +28,8 @@ package me.lucko.luckperms.sponge.service.model.calculated; import com.google.common.collect.ImmutableList; import me.lucko.luckperms.common.cacheddata.AbstractCachedDataManager; import me.lucko.luckperms.common.cacheddata.CacheMetadata; +import me.lucko.luckperms.common.cacheddata.metastack.SimpleMetaStackDefinition; +import me.lucko.luckperms.common.cacheddata.metastack.StandardStackElements; import me.lucko.luckperms.common.cacheddata.type.MetaAccumulator; import me.lucko.luckperms.common.calculator.CalculatorFactory; import me.lucko.luckperms.common.calculator.PermissionCalculator; @@ -35,8 +37,6 @@ import me.lucko.luckperms.common.calculator.processor.DirectProcessor; import me.lucko.luckperms.common.calculator.processor.PermissionProcessor; import me.lucko.luckperms.common.calculator.processor.SpongeWildcardProcessor; import me.lucko.luckperms.common.calculator.processor.WildcardProcessor; -import me.lucko.luckperms.common.metastacking.SimpleMetaStackDefinition; -import me.lucko.luckperms.common.metastacking.StandardStackElements; import me.lucko.luckperms.common.plugin.LuckPermsPlugin; import me.lucko.luckperms.common.verbose.VerboseCheckTarget; import me.lucko.luckperms.sponge.calculator.FixedTypeDefaultsProcessor;