1
0
mirror of https://github.com/lucko/LuckPerms.git synced 2025-08-30 17:49:48 +02:00

Add more unit tests

This commit is contained in:
Luck
2024-12-22 09:50:25 +00:00
parent f2040ab815
commit 0fef481d48
46 changed files with 1358 additions and 154 deletions

View File

@@ -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

View File

@@ -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'

View File

@@ -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;

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -0,0 +1,57 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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<T, N extends Node, S extends AbstractResult<T, N, S>> implements Result<T, N> {
/** 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;
}
}

View File

@@ -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 <N> the node type
*/
public final class IntegerResult<N extends Node> implements Result<Integer, N> {
public final class IntegerResult<N extends Node> extends AbstractResult<Integer, N, IntegerResult<N>> {
/** 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<N> overriddenResult;
public IntegerResult(int result, N node, IntegerResult<N> overriddenResult) {
super(node, overriddenResult);
this.result = result;
this.node = node;
this.overriddenResult = overriddenResult;
}
@Override
@@ -73,19 +66,6 @@ public final class IntegerResult<N extends Node> implements Result<Integer, N> {
}
}
@Override
public @Nullable N node() {
return this.node;
}
public @Nullable IntegerResult<N> overriddenResult() {
return this.overriddenResult;
}
public void setOverriddenResult(IntegerResult<N> overriddenResult) {
this.overriddenResult = overriddenResult;
}
public boolean isNull() {
return this == NULL_RESULT;
}

View File

@@ -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 <N> the node type
*/
public final class StringResult<N extends Node> implements Result<String, N> {
public final class StringResult<N extends Node> extends AbstractResult<String, N, StringResult<N>> {
/** 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<N> overriddenResult;
public StringResult(String result, N node, StringResult<N> overriddenResult) {
super(node, overriddenResult);
this.result = result;
this.node = node;
this.overriddenResult = overriddenResult;
}
@Override
@@ -56,19 +50,6 @@ public final class StringResult<N extends Node> implements Result<String, N> {
return this.result;
}
@Override
public @Nullable N node() {
return this.node;
}
public @Nullable StringResult<N> overriddenResult() {
return this.overriddenResult;
}
public void setOverriddenResult(StringResult<N> overriddenResult) {
this.overriddenResult = overriddenResult;
}
public StringResult<N> copy() {
return new StringResult<>(this.result, this.node, this.overriddenResult);
}

View File

@@ -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<Tristate, Node> {
public final class TristateResult extends AbstractResult<Tristate, Node, TristateResult> {
/** 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<? extends PermissionProcessor> processorClass;
/** A reference to another result that this one overrides */
private TristateResult overriddenResult;
private TristateResult(Tristate result, Node node, Class<? extends PermissionProcessor> processorClass) {
super(node, null);
this.result = result;
this.node = node;
this.processorClass = processorClass;
}
@@ -58,11 +53,6 @@ public final class TristateResult implements Result<Tristate, Node> {
return this.result;
}
@Override
public @Nullable Node node() {
return this.node;
}
public @Nullable Class<? extends PermissionProcessor> processorClass() {
return this.processorClass;
}
@@ -79,14 +69,6 @@ public final class TristateResult implements Result<Tristate, Node> {
}
}
public @Nullable TristateResult overriddenResult() {
return this.overriddenResult;
}
public void setOverriddenResult(TristateResult overriddenResult) {
this.overriddenResult = overriddenResult;
}
@Override
public String toString() {
return "TristateResult(" +

View File

@@ -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<T> extends Command<T> {
.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();
}

View File

@@ -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();
}

View File

@@ -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<Void> {
.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();
}

View File

@@ -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<MetaStackDefinition> PREFIX_FORMATTING_OPTIONS = key(l -> {
List<String> format = l.getStringList("meta-formatting.prefix.format", new ArrayList<>());
public static final ConfigKey<MetaStackDefinition> PREFIX_FORMATTING_OPTIONS = key(c -> {
List<String> 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<MetaStackDefinition> SUFFIX_FORMATTING_OPTIONS = key(l -> {
List<String> format = l.getStringList("meta-formatting.suffix.format", new ArrayList<>());
public static final ConfigKey<MetaStackDefinition> SUFFIX_FORMATTING_OPTIONS = key(c -> {
List<String> 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);
});
/**

View File

@@ -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

View File

@@ -210,12 +210,17 @@ public enum ShorthandParser {
* Implements an iterator over a given range of ints.
*/
private abstract static class RangeIterator implements Iterator<String> {
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);

View File

@@ -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());
}

View File

@@ -54,7 +54,7 @@ final class FlagUtils {
private static byte toByte0(Set<Flag> settings) {
byte b = 0;
for (Flag setting : settings) {
b |= 1 << setting.ordinal();
b |= (byte) (1 << setting.ordinal());
}
return b;
}

View File

@@ -56,7 +56,7 @@ public class QueryOptionsImpl implements QueryOptions {
// computed based on state above
private final int hashCode;
private Set<Flag> flagsSet = null;
private final ContextSatisfyMode contextSatisfyMode;
private final ContextSatisfyMode overrideContextSatisfyMode;
QueryOptionsImpl(QueryMode mode, @Nullable ImmutableContextSet context, byte flags, @Nullable Map<OptionKey<?>, 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:

View File

@@ -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<T> implements Sender {
case 1:
return input.get(0);
default:
return Component.join(Component.empty(), input);
return Component.join(JoinConfiguration.separator(Component.empty()), input);
}
});
}

View File

@@ -0,0 +1,70 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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<String> 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();
}
}

View File

@@ -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<String> 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

View File

@@ -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)));
}
/**

View File

@@ -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<Component> hoverEvent = HoverEvent.showText(Component.join(Component.newline(), hover));
HoverEvent<Component> hoverEvent = HoverEvent.showText(Component.join(JoinConfiguration.newlines(), hover));
this.notifiedSender.sendMessage(component.hoverEvent(hoverEvent));
}

View File

@@ -0,0 +1,168 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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<PrefixNode> 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<String, StringResult<MetaNode>> meta = accumulator.getMeta();
assertEquals(0, meta.size());
SortedMap<Integer, StringResult<PrefixNode>> prefixes = accumulator.getPrefixes();
assertEquals(0, prefixes.size());
SortedMap<Integer, StringResult<SuffixNode>> suffixes = accumulator.getSuffixes();
assertEquals(0, suffixes.size());
IntegerResult<WeightNode> 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<PrefixNode> prefix = accumulator.getPrefix();
assertNull(prefix.result());
assertNull(prefix.node());
StringResult<SuffixNode> 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<PrefixNode> prefix = accumulator.getPrefix();
assertEquals("[a]", prefix.result());
SortedMap<Integer, StringResult<PrefixNode>> prefixes = accumulator.getPrefixes();
assertEquals(ImmutableSet.of(100, 90, 80), prefixes.keySet());
StringResult<SuffixNode> suffix = accumulator.getSuffix();
assertEquals("[foo]", suffix.result());
SortedMap<Integer, StringResult<SuffixNode>> suffixes = accumulator.getSuffixes();
assertEquals(ImmutableSet.of(80), suffixes.keySet());
ListMultimap<String, StringResult<MetaNode>> 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<WeightNode> weight = accumulator.getWeight();
assertEquals(5, weight.intResult());
String primaryGroup = accumulator.getPrimaryGroup();
assertEquals("member", primaryGroup);
}
}

View File

@@ -0,0 +1,104 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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<PrefixNode> accumulator = new MetaStackAccumulator<>(definition, ChatMetaType.PREFIX);
StringResult<PrefixNode> 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<PrefixNode> 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<PrefixNode> 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<PrefixNode> 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<PrefixNode> result = accumulator.toResult();
assertNotNull(result);
assertEquals("[c|a]", result.result());
assertEquals(c, result.node());
StringResult<PrefixNode> overriddenResult = result.overriddenResult();
assertNotNull(overriddenResult);
assertEquals(a, overriddenResult.node());
assertNull(overriddenResult.overriddenResult());
}
}

View File

@@ -0,0 +1,243 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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<Track> 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<Track> 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
));
}
}

View File

@@ -0,0 +1,81 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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<String, Strategy> 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<String, MetaNode> foo = StringResult.of("foo");
// single value
Result<String, MetaNode> 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<String, MetaNode> one = StringResult.of("1");
Result<String, MetaNode> two = StringResult.of("2");
Result<String, MetaNode> three = StringResult.of("3");
ImmutableList<Result<String, MetaNode>> 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));
}
}

View File

@@ -0,0 +1,54 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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;
}
}
}

View File

@@ -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<Object, String> {
FOO, BAR;
FOO, BAR, BAZ;
@Override
public String getValue(Object object) {

View File

@@ -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<Object> filters, String expectedSql, String expectedSqlParams) {
TestFilterMongoBuilder sqlBuilder = new TestFilterMongoBuilder();
FilterSqlBuilder<Object> sqlBuilder = new TestFilterSqlBuilder();
sqlBuilder.visit(filters);
System.out.println(sqlBuilder.builder().toReadableString());
@@ -91,7 +101,7 @@ public class FilterSqlTest {
}
private enum TestField implements FilterField<Object, String> {
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<Object> {
private static final class TestFilterSqlBuilder extends FilterSqlBuilder<Object> {
@Override
public void visitFieldName(FilterField<Object, ?> field) {

View File

@@ -0,0 +1,71 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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<Arguments> 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);
}
}

View File

@@ -0,0 +1,111 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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<Node> matcher = StandardNodeMatchers.key("foo");
Constraint<String> constraint = matcher.getConstraint();
assertEquals(Comparison.EQUAL, constraint.comparison());
assertEquals("foo", constraint.value());
ConstraintNodeMatcher<Permission> typedMatcher = StandardNodeMatchers.key(Permission.builder().permission("foo").build());
Constraint<String> typedConstraint = typedMatcher.getConstraint();
assertEquals(Comparison.EQUAL, typedConstraint.comparison());
assertEquals("foo", typedConstraint.value());
}
@ParameterizedTest
@CsvSource({
"foo, foo%",
})
public void testKeyStartsWith(String value, String expectedConstraint) {
ConstraintNodeMatcher<Node> matcher = StandardNodeMatchers.keyStartsWith(value);
Constraint<String> 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<MetaNode> matcher = StandardNodeMatchers.metaKey(value);
Constraint<String> constraint = matcher.getConstraint();
assertEquals(Comparison.SIMILAR, constraint.comparison());
assertEquals(expectedConstraint, constraint.value());
}
private static Stream<Arguments> 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<String> constraint = matcher.getConstraint();
assertEquals(Comparison.SIMILAR, constraint.comparison());
assertEquals(expectedValue, constraint.value());
}
}

View File

@@ -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);

View File

@@ -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()

View File

@@ -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<Arguments> 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);
}
}

View File

@@ -0,0 +1,84 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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);
}
}

View File

@@ -0,0 +1,62 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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));
}
}
}
}

View File

@@ -0,0 +1,81 @@
/*
* This file is part of LuckPerms, licensed under the MIT License.
*
* Copyright (c) lucko (Luck) <luck@lucko.me>
* 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<String> 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());
}
}

View File

@@ -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)));
}
});
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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<String> 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

View File

@@ -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;