-group = 'net.pterodactylus'
-version = '80'
-buildscript {
- ext.kotlinVersion = '1.3.41'
- repositories {
- mavenCentral()
- }
- dependencies {
- classpath group: 'info.solidsoft.gradle.pitest', name: 'gradle-pitest-plugin', version: '1.4.0'
- classpath group: 'org.jetbrains.kotlin', name: 'kotlin-gradle-plugin', version: kotlinVersion
- classpath group: 'org.jetbrains.kotlin', name: 'kotlin-noarg', version: kotlinVersion
- }
+plugins {
+ id 'org.jetbrains.kotlin.jvm' version '1.3.50'
+ id 'org.jetbrains.kotlin.plugin.noarg' version '1.3.50'
+ id 'info.solidsoft.pitest' version '1.4.5'
}
+group = 'net.pterodactylus'
+version = '80'
+
repositories {
mavenCentral()
maven { url "https://maven.pterodactylus.net/" }
options.encoding = 'UTF-8'
}
-apply plugin: 'kotlin'
-
configurations {
provided {
dependencies.all { dep ->
jacocoTestReport.dependsOn test
-apply plugin: 'info.solidsoft.pitest'
-
pitest {
+ pitestVersion = '1.4.10'
outputFormats = ['HTML', 'XML']
timestampedReports = false
timeoutFactor = 3.0
dependsOn tasks.countLinesTest
}
-apply plugin: 'kotlin-noarg'
-
noArg {
annotation('net.pterodactylus.sone.main.NoArg')
}
import net.pterodactylus.util.thread.NamedThreadFactory;
import com.google.common.annotations.VisibleForTesting;
+import com.google.common.base.Stopwatch;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
private volatile long lastConfigurationUpdate;
private final MetricRegistry metricRegistry;
+ private final Histogram configurationSaveTimeHistogram;
/**
* Creates a new core.
this.database = database;
this.metricRegistry = metricRegistry;
preferences = new Preferences(eventBus);
+ this.configurationSaveTimeHistogram = metricRegistry.histogram("configuration.save.duration", () -> new Histogram(new ExponentiallyDecayingReservoir(3000, 0)));
}
//
loadSone(sone);
database.storeSone(sone);
sone.setStatus(SoneStatus.idle);
+ if (sone.getPosts().isEmpty() && sone.getReplies().isEmpty()) {
+ // dirty hack
+ lockSone(sone);
+ }
soneInserter.start();
return sone;
}
synchronized (soneInserters) {
for (Entry<Sone, SoneInserter> soneInserter : soneInserters.entrySet()) {
soneInserter.getValue().stop();
- saveSone(soneInserter.getKey());
+ Sone latestSone = getLocalSone(soneInserter.getKey().getId());
+ saveSone(latestSone);
}
}
synchronized (soneRescuers) {
database.save();
/* now save it. */
+ Stopwatch stopwatch = Stopwatch.createStarted();
configuration.save();
+ configurationSaveTimeHistogram.update(stopwatch.elapsed(TimeUnit.MICROSECONDS));
} catch (ConfigurationException ce1) {
logger.log(Level.SEVERE, "Could not store configuration!", ce1);
*/
@Subscribe
public void ownIdentityAdded(OwnIdentityAddedEvent ownIdentityAddedEvent) {
- OwnIdentity ownIdentity = ownIdentityAddedEvent.ownIdentity();
+ OwnIdentity ownIdentity = ownIdentityAddedEvent.getOwnIdentity();
logger.log(Level.FINEST, String.format("Adding OwnIdentity: %s", ownIdentity));
if (ownIdentity.hasContext("Sone")) {
addLocalSone(ownIdentity);
*/
@Subscribe
public void ownIdentityRemoved(OwnIdentityRemovedEvent ownIdentityRemovedEvent) {
- OwnIdentity ownIdentity = ownIdentityRemovedEvent.ownIdentity();
+ OwnIdentity ownIdentity = ownIdentityRemovedEvent.getOwnIdentity();
logger.log(Level.FINEST, String.format("Removing OwnIdentity: %s", ownIdentity));
trustedIdentities.removeAll(ownIdentity);
}
*/
@Subscribe
public void identityAdded(IdentityAddedEvent identityAddedEvent) {
- Identity identity = identityAddedEvent.identity();
+ Identity identity = identityAddedEvent.getIdentity();
logger.log(Level.FINEST, String.format("Adding Identity: %s", identity));
- trustedIdentities.put(identityAddedEvent.ownIdentity(), identity);
+ trustedIdentities.put(identityAddedEvent.getOwnIdentity(), identity);
addRemoteSone(identity);
}
*/
@Subscribe
public void identityUpdated(IdentityUpdatedEvent identityUpdatedEvent) {
- Identity identity = identityUpdatedEvent.identity();
+ Identity identity = identityUpdatedEvent.getIdentity();
final Sone sone = getRemoteSone(identity.getId());
if (sone.isLocal()) {
return;
*/
@Subscribe
public void identityRemoved(IdentityRemovedEvent identityRemovedEvent) {
- OwnIdentity ownIdentity = identityRemovedEvent.ownIdentity();
- Identity identity = identityRemovedEvent.identity();
+ OwnIdentity ownIdentity = identityRemovedEvent.getOwnIdentity();
+ Identity identity = identityRemovedEvent.getIdentity();
trustedIdentities.remove(ownIdentity, identity);
for (Entry<OwnIdentity, Collection<Identity>> trustedIdentity : trustedIdentities.asMap().entrySet()) {
if (trustedIdentity.getKey().equals(ownIdentity)) {
import static java.lang.String.format;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
-import static net.pterodactylus.sone.freenet.Key.routingKey;
import java.io.IOException;
import java.net.MalformedURLException;
import freenet.support.api.RandomAccessBucket;
import freenet.support.io.ArrayBucket;
import freenet.support.io.ResumeFailedException;
+import net.pterodactylus.sone.freenet.*;
/**
* Contains all necessary functionality for interacting with the Freenet node.
public void registerActiveUsk(FreenetURI requestUri,
USKCallback uskCallback) {
try {
- soneUskCallbacks.put(routingKey(requestUri), uskCallback);
+ soneUskCallbacks.put(FreenetURIsKt.getRoutingKeyString(requestUri), uskCallback);
node.clientCore.uskManager.subscribe(create(requestUri),
uskCallback, true, requestClient);
} catch (MalformedURLException mue1) {
public void registerPassiveUsk(FreenetURI requestUri,
USKCallback uskCallback) {
try {
- soneUskCallbacks.put(routingKey(requestUri), uskCallback);
+ soneUskCallbacks.put(FreenetURIsKt.getRoutingKeyString(requestUri), uskCallback);
node.clientCore
.uskManager
.subscribe(create(requestUri), uskCallback, false, requestClient);
private final Ticker ticker;
private final LockableFingerprintProvider lockableFingerprintProvider;
private final AtomicInteger insertionDelay;
- private Optional<Long> lastModificationTime;
+ private Long lastModificationTime;
private String lastInsertFingerprint;
private String lastCheckFingerprint;
public boolean isEligibleForInsert() {
if (lockableFingerprintProvider.isLocked()) {
- lastModificationTime = absent();
+ lastModificationTime = null;
lastCheckFingerprint = "";
return false;
}
String fingerprint = lockableFingerprintProvider.getFingerprint();
if (fingerprint.equals(lastInsertFingerprint)) {
- lastModificationTime = absent();
+ lastModificationTime = null;
lastCheckFingerprint = fingerprint;
return false;
}
if (!Objects.equal(lastCheckFingerprint, fingerprint)) {
- lastModificationTime = of(ticker.read());
+ lastModificationTime = ticker.read();
lastCheckFingerprint = fingerprint;
return false;
}
public void setFingerprint(String fingerprint) {
lastInsertFingerprint = fingerprint;
lastCheckFingerprint = lastInsertFingerprint;
- lastModificationTime = absent();
+ lastModificationTime = null;
}
private boolean insertionDelayHasPassed() {
- return NANOSECONDS.toSeconds(ticker.read() - lastModificationTime.get()) >= insertionDelay.get();
+ return NANOSECONDS.toSeconds(ticker.read() - lastModificationTime) >= insertionDelay.get();
}
public boolean isModified() {
@Inject
public SoneParser(Database database, MetricRegistry metricRegistry) {
this.database = database;
- this.soneParsingDurationHistogram = metricRegistry.histogram("sone.parsing.duration", () -> new Histogram(new ExponentiallyDecayingReservoir(3000, 0)));
+ this.soneParsingDurationHistogram = metricRegistry.histogram("sone.parse.duration", () -> new Histogram(new ExponentiallyDecayingReservoir(3000, 0)));
}
@Nullable
}
/**
+ * Sets the edition to rescue.
+ *
+ * @param edition
+ * The edition to rescue
+ * @return This Sone rescuer
+ */
+ public SoneRescuer setEdition(long edition) {
+ currentEdition = edition;
+ return this;
+ }
+
+ /**
* Sets whether the last fetch was successful.
*
* @return {@code true} if the last fetch was successful, {@code false}
//
/**
- * Starts the next fetch.
+ * Starts the next fetch. If you want to fetch a different edition than “the
+ * next older one,” remember to call {@link #setEdition(long)} before
+ * calling this method.
*/
public void startNextFetch() {
fetching = true;
}
if (fetching) {
core.lockSone(sone);
- FreenetURI soneUri = sone.getRequestUri().setKeyType("SSK").setDocName("Sone-" + getNextEdition()).setMetaString(new String[] { "sone.xml" });
+ FreenetURI soneUri = sone.getRequestUri().setKeyType("SSK").setDocName("Sone-" + currentEdition).setMetaString(new String[] { "sone.xml" });
System.out.println("URI: " + soneUri);
Sone fetchedSone = soneDownloader.fetchSone(sone, soneUri, true);
System.out.println("Sone: " + fetchedSone);
lastFetchSuccessful = (fetchedSone != null);
if (lastFetchSuccessful) {
core.updateSone(fetchedSone, true);
- currentEdition = getNextEdition();
}
fetching = false;
}
package net.pterodactylus.sone.data.impl;
-import static com.google.common.base.Optional.absent;
-import static com.google.common.base.Optional.fromNullable;
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static java.nio.charset.StandardCharsets.UTF_8;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.UUID;
-
-import net.pterodactylus.sone.data.Album;
-import net.pterodactylus.sone.data.Image;
-import net.pterodactylus.sone.data.Sone;
-
-import com.google.common.base.Function;
-import com.google.common.base.Optional;
-import com.google.common.base.Predicates;
-import com.google.common.collect.Collections2;
-import com.google.common.hash.Hasher;
+import java.util.*;
+import javax.annotation.*;
+
+import com.google.common.base.*;
+import com.google.common.collect.*;
import com.google.common.hash.Hashing;
+import com.google.common.hash.*;
+import net.pterodactylus.sone.data.*;
+
+import static com.google.common.base.Preconditions.*;
+import static java.nio.charset.StandardCharsets.*;
/**
* Container for images that can also contain nested {@link AlbumImpl}s.
public Modifier modify() throws IllegalStateException {
// TODO: reenable check for local Sones
return new Modifier() {
- private Optional<String> title = absent();
-
- private Optional<String> description = absent();
+ @Nullable
+ private String title;
+ @Nullable
+ private String description;
@Override
public Modifier setTitle(String title) {
- this.title = fromNullable(title);
+ this.title = title;
return this;
}
@Override
public Modifier setDescription(String description) {
- this.description = fromNullable(description);
+ this.description = description;
return this;
}
@Override
public Album update() throws IllegalStateException {
- if (title.isPresent() && title.get().trim().isEmpty()) {
+ if (title != null && title.trim().isEmpty()) {
throw new AlbumTitleMustNotBeEmpty();
}
- if (title.isPresent()) {
- AlbumImpl.this.title = title.get();
+ if (title != null) {
+ AlbumImpl.this.title = title;
}
- if (description.isPresent()) {
- AlbumImpl.this.description = description.get();
+ if (description != null) {
+ AlbumImpl.this.description = description;
}
return AlbumImpl.this;
}
*/
package net.pterodactylus.sone.data.impl;
-import static com.google.common.base.Optional.absent;
-import static com.google.common.base.Optional.fromNullable;
-import static com.google.common.base.Optional.of;
-import static com.google.common.base.Preconditions.checkNotNull;
-import static com.google.common.base.Preconditions.checkState;
-import static java.nio.charset.StandardCharsets.UTF_8;
+import java.util.*;
+import javax.annotation.*;
-import java.util.UUID;
+import com.google.common.hash.*;
+import net.pterodactylus.sone.data.*;
-import net.pterodactylus.sone.data.Album;
-import net.pterodactylus.sone.data.Image;
-import net.pterodactylus.sone.data.Sone;
-
-import com.google.common.base.Optional;
-import com.google.common.hash.Hasher;
-import com.google.common.hash.Hashing;
+import static com.google.common.base.Preconditions.*;
+import static java.nio.charset.StandardCharsets.*;
/**
* Container for image metadata.
public Modifier modify() throws IllegalStateException {
// TODO: reenable check for local images
return new Modifier() {
- private Optional<Sone> sone = absent();
-
- private Optional<Long> creationTime = absent();
-
- private Optional<String> key = absent();
-
- private Optional<String> title = absent();
-
- private Optional<String> description = absent();
-
- private Optional<Integer> width = absent();
-
- private Optional<Integer> height = absent();
+ @Nullable
+ private Sone sone;
+ @Nullable
+ private Long creationTime;
+ @Nullable
+ private String key;
+ @Nullable
+ private String title;
+ @Nullable
+ private String description;
+ @Nullable
+ private Integer width;
+ @Nullable
+ private Integer height;
@Override
public Modifier setSone(Sone sone) {
- this.sone = fromNullable(sone);
+ this.sone = sone;
return this;
}
@Override
public Modifier setCreationTime(long creationTime) {
- this.creationTime = of(creationTime);
+ this.creationTime = creationTime;
return this;
}
@Override
public Modifier setKey(String key) {
- this.key = fromNullable(key);
+ this.key = key;
return this;
}
@Override
public Modifier setTitle(String title) {
- this.title = fromNullable(title);
+ this.title = title;
return this;
}
@Override
public Modifier setDescription(String description) {
- this.description = fromNullable(description);
+ this.description = description;
return this;
}
@Override
public Modifier setWidth(int width) {
- this.width = of(width);
+ this.width = width;
return this;
}
@Override
public Modifier setHeight(int height) {
- this.height = of(height);
+ this.height = height;
return this;
}
@Override
public Image update() throws IllegalStateException {
- checkState(!sone.isPresent() || (ImageImpl.this.sone == null) || sone.get().equals(ImageImpl.this.sone), "can not change Sone once set");
- checkState(!creationTime.isPresent() || ((ImageImpl.this.creationTime == 0) || (ImageImpl.this.creationTime == creationTime.get())), "can not change creation time once set");
- checkState(!key.isPresent() || (ImageImpl.this.key == null) || key.get().equals(ImageImpl.this.key), "can not change key once set");
- if (title.isPresent() && title.get().trim().isEmpty()) {
+ checkState(sone == null || (ImageImpl.this.sone == null) || sone.equals(ImageImpl.this.sone), "can not change Sone once set");
+ checkState(creationTime == null || ((ImageImpl.this.creationTime == 0) || (ImageImpl.this.creationTime == creationTime)), "can not change creation time once set");
+ checkState(key == null || (ImageImpl.this.key == null) || key.equals(ImageImpl.this.key), "can not change key once set");
+ if (title != null && title.trim().isEmpty()) {
throw new ImageTitleMustNotBeEmpty();
}
- checkState(!width.isPresent() || (ImageImpl.this.width == 0) || width.get().equals(ImageImpl.this.width), "can not change width once set");
- checkState(!height.isPresent() || (ImageImpl.this.height == 0) || height.get().equals(ImageImpl.this.height), "can not change height once set");
+ checkState(width == null || (ImageImpl.this.width == 0) || width.equals(ImageImpl.this.width), "can not change width once set");
+ checkState(height == null || (ImageImpl.this.height == 0) || height.equals(ImageImpl.this.height), "can not change height once set");
- if (sone.isPresent()) {
- ImageImpl.this.sone = sone.get();
+ if (sone != null) {
+ ImageImpl.this.sone = sone;
}
- if (creationTime.isPresent()) {
- ImageImpl.this.creationTime = creationTime.get();
+ if (creationTime != null) {
+ ImageImpl.this.creationTime = creationTime;
}
- if (key.isPresent()) {
- ImageImpl.this.key = key.get();
+ if (key != null) {
+ ImageImpl.this.key = key;
}
- if (title.isPresent()) {
- ImageImpl.this.title = title.get();
+ if (title != null) {
+ ImageImpl.this.title = title;
}
- if (description.isPresent()) {
- ImageImpl.this.description = description.get();
+ if (description != null) {
+ ImageImpl.this.description = description;
}
- if (width.isPresent()) {
- ImageImpl.this.width = width.get();
+ if (width != null) {
+ ImageImpl.this.width = width;
}
- if (height.isPresent()) {
- ImageImpl.this.height = height.get();
+ if (height != null) {
+ ImageImpl.this.height = height;
}
return ImageImpl.this;
import net.pterodactylus.sone.database.PostBuilder
import net.pterodactylus.sone.database.PostDatabase
import net.pterodactylus.sone.database.PostReplyBuilder
-import net.pterodactylus.sone.utils.unit
+import net.pterodactylus.sone.utils.*
import net.pterodactylus.util.config.Configuration
import net.pterodactylus.util.config.ConfigurationException
import java.util.concurrent.locks.ReentrantReadWriteLock
private val memoryBookmarkDatabase = MemoryBookmarkDatabase(this, configurationLoader)
private val memoryFriendDatabase = MemoryFriendDatabase(configurationLoader)
private val saveRateLimiter: RateLimiter = RateLimiter.create(1.0)
+ private val saveKnownPostsRateLimiter: RateLimiter = RateLimiter.create(1.0)
+ private val saveKnownPostRepliesRateLimiter: RateLimiter = RateLimiter.create(1.0)
override val soneLoader get() = this::getSone
}
private fun saveKnownPosts() =
- try {
- readLock.withLock {
- knownPosts.forEachIndexed { index, knownPostId ->
- configuration.getStringValue("KnownPosts/$index/ID").value = knownPostId
+ saveKnownPostsRateLimiter.tryAcquire().ifTrue {
+ try {
+ readLock.withLock {
+ knownPosts.forEachIndexed { index, knownPostId ->
+ configuration.getStringValue("KnownPosts/$index/ID").value = knownPostId
+ }
+ configuration.getStringValue("KnownPosts/${knownPosts.size}/ID").value = null
}
- configuration.getStringValue("KnownPosts/${knownPosts.size}/ID").value = null
+ } catch (ce1: ConfigurationException) {
+ throw DatabaseException("Could not save database.", ce1)
}
- } catch (ce1: ConfigurationException) {
- throw DatabaseException("Could not save database.", ce1)
}
private fun loadKnownPostReplies(): Unit =
}
private fun saveKnownPostReplies() =
- try {
- readLock.withLock {
- knownPostReplies.forEachIndexed { index, knownPostReply ->
- configuration.getStringValue("KnownReplies/$index/ID").value = knownPostReply
+ saveKnownPostRepliesRateLimiter.tryAcquire().ifTrue {
+ try {
+ readLock.withLock {
+ knownPostReplies.forEachIndexed { index, knownPostReply ->
+ configuration.getStringValue("KnownReplies/$index/ID").value = knownPostReply
+ }
+ configuration.getStringValue("KnownReplies/${knownPostReplies.size}/ID").value = null
}
- configuration.getStringValue("KnownReplies/${knownPostReplies.size}/ID").value = null
+ } catch (ce1: ConfigurationException) {
+ throw DatabaseException("Could not save database.", ce1)
}
- } catch (ce1: ConfigurationException) {
- throw DatabaseException("Could not save database.", ce1)
}
}
+++ /dev/null
-package net.pterodactylus.sone.freenet;
-
-import static freenet.support.Base64.encode;
-import static java.lang.String.format;
-
-import freenet.keys.FreenetURI;
-
-import com.google.common.annotations.VisibleForTesting;
-
-/**
- * Encapsulates the parts of a {@link FreenetURI} that do not change while
- * being converted from SSK to USK and/or back.
- */
-public class Key {
-
- private final byte[] routingKey;
- private final byte[] cryptoKey;
- private final byte[] extra;
-
- private Key(byte[] routingKey, byte[] cryptoKey, byte[] extra) {
- this.routingKey = routingKey;
- this.cryptoKey = cryptoKey;
- this.extra = extra;
- }
-
- @VisibleForTesting
- public String getRoutingKey() {
- return encode(routingKey);
- }
-
- @VisibleForTesting
- public String getCryptoKey() {
- return encode(cryptoKey);
- }
-
- @VisibleForTesting
- public String getExtra() {
- return encode(extra);
- }
-
- public FreenetURI toUsk(String docName, long edition, String... paths) {
- return new FreenetURI("USK", docName, paths, routingKey, cryptoKey,
- extra, edition);
- }
-
- public FreenetURI toSsk(String docName, String... paths) {
- return new FreenetURI("SSK", docName, paths, routingKey, cryptoKey,
- extra);
- }
-
- public FreenetURI toSsk(String docName, long edition, String... paths) {
- return new FreenetURI("SSK", format("%s-%d", docName, edition), paths,
- routingKey, cryptoKey, extra, edition);
- }
-
- public static Key from(FreenetURI freenetURI) {
- return new Key(freenetURI.getRoutingKey(), freenetURI.getCryptoKey(),
- freenetURI.getExtra());
- }
-
- public static String routingKey(FreenetURI freenetURI) {
- return from(freenetURI).getRoutingKey();
- }
-
-}
+++ /dev/null
-/*
- * Sone - L10nFilter.java - Copyright © 2010–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet;
-
-import java.text.MessageFormat;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Locale;
-import java.util.Map;
-
-import javax.annotation.Nonnull;
-
-import net.pterodactylus.util.template.Filter;
-import net.pterodactylus.util.template.TemplateContext;
-
-import freenet.l10n.BaseL10n;
-
-/**
- * {@link Filter} implementation replaces {@link String} values with their
- * translated equivalents.
- */
-public class L10nFilter implements Filter {
-
- private final BaseL10n l10n;
-
- public L10nFilter(BaseL10n l10n) {
- this.l10n = l10n;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public String format(TemplateContext templateContext, Object data, Map<String, Object> parameters) {
- List<Object> parameterValues = getParameters(data, parameters);
- String text = getText(data);
- if (parameterValues.isEmpty()) {
- return l10n.getString(text);
- }
- return new MessageFormat(l10n.getString(text), new Locale(l10n.getSelectedLanguage().shortCode)).format(parameterValues.toArray());
- }
-
- @Nonnull
- private String getText(Object data) {
- return (data instanceof L10nText) ? ((L10nText) data).getText() : String.valueOf(data);
- }
-
- @Nonnull
- private List<Object> getParameters(Object data, Map<String, Object> parameters) {
- if (data instanceof L10nText) {
- return ((L10nText) data).getParameters();
- }
- List<Object> parameterValues = new ArrayList<>();
- int parameterIndex = 0;
- while (parameters.containsKey(String.valueOf(parameterIndex))) {
- Object value = parameters.get(String.valueOf(parameterIndex));
- parameterValues.add(value);
- ++parameterIndex;
- }
- return parameterValues;
- }
-
-}
+++ /dev/null
-/*
- * Sone - PluginConnector.java - Copyright © 2010–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.plugin;
-
-import net.pterodactylus.sone.freenet.plugin.event.ReceivedReplyEvent;
-
-import com.google.common.eventbus.EventBus;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import freenet.pluginmanager.FredPluginTalker;
-import freenet.pluginmanager.PluginNotFoundException;
-import freenet.pluginmanager.PluginRespirator;
-import freenet.pluginmanager.PluginTalker;
-import freenet.support.SimpleFieldSet;
-import freenet.support.api.Bucket;
-
-/**
- * Interface for talking to other plugins. Other plugins are identified by their
- * name and a unique connection identifier.
- */
-@Singleton
-public class PluginConnector implements FredPluginTalker {
-
- /** The event bus. */
- private final EventBus eventBus;
-
- /** The plugin respirator. */
- private final PluginRespirator pluginRespirator;
-
- /**
- * Creates a new plugin connector.
- *
- * @param eventBus
- * The event bus
- * @param pluginRespirator
- * The plugin respirator
- */
- @Inject
- public PluginConnector(EventBus eventBus, PluginRespirator pluginRespirator) {
- this.eventBus = eventBus;
- this.pluginRespirator = pluginRespirator;
- }
-
- //
- // ACTIONS
- //
-
- /**
- * Sends a request to the given plugin.
- *
- * @param pluginName
- * The name of the plugin
- * @param identifier
- * The identifier of the connection
- * @param fields
- * The fields of the message
- * @throws PluginException
- * if the plugin can not be found
- */
- public void sendRequest(String pluginName, String identifier, SimpleFieldSet fields) throws PluginException {
- sendRequest(pluginName, identifier, fields, null);
- }
-
- /**
- * Sends a request to the given plugin.
- *
- * @param pluginName
- * The name of the plugin
- * @param identifier
- * The identifier of the connection
- * @param fields
- * The fields of the message
- * @param data
- * The payload of the message (may be null)
- * @throws PluginException
- * if the plugin can not be found
- */
- public void sendRequest(String pluginName, String identifier, SimpleFieldSet fields, Bucket data) throws PluginException {
- getPluginTalker(pluginName, identifier).send(fields, data);
- }
-
- //
- // PRIVATE METHODS
- //
-
- /**
- * Returns the plugin talker for the given plugin connection.
- *
- * @param pluginName
- * The name of the plugin
- * @param identifier
- * The identifier of the connection
- * @return The plugin talker
- * @throws PluginException
- * if the plugin can not be found
- */
- private PluginTalker getPluginTalker(String pluginName, String identifier) throws PluginException {
- try {
- return pluginRespirator.getPluginTalker(this, pluginName, identifier);
- } catch (PluginNotFoundException pnfe1) {
- throw new PluginException(pnfe1);
- }
- }
-
- //
- // INTERFACE FredPluginTalker
- //
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void onReply(String pluginName, String identifier, SimpleFieldSet params, Bucket data) {
- eventBus.post(new ReceivedReplyEvent(this, pluginName, identifier, params, data));
- }
-
-}
+++ /dev/null
-/*
- * Sone - PluginException.java - Copyright © 2010–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.plugin;
-
-import net.pterodactylus.sone.freenet.wot.WebOfTrustException;
-
-/**
- * Exception that signals an error when communicating with a plugin.
- */
-public class PluginException extends WebOfTrustException {
-
- /**
- * Creates a new plugin exception.
- */
- public PluginException() {
- super();
- }
-
- /**
- * Creates a new plugin exception.
- *
- * @param message
- * The message of the exception
- */
- public PluginException(String message) {
- super(message);
- }
-
- /**
- * Creates a new plugin exception.
- *
- * @param cause
- * The cause of the exception
- */
- public PluginException(Throwable cause) {
- super(cause);
- }
-
- /**
- * Creates a new plugin exception.
- *
- * @param message
- * The message of the exception
- * @param cause
- * The cause of the exception
- */
- public PluginException(String message, Throwable cause) {
- super(message, cause);
- }
-
-}
+++ /dev/null
-/*
- * Sone - ReceivedReplyEvent.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.plugin.event;
-
-import net.pterodactylus.sone.freenet.plugin.PluginConnector;
-import freenet.support.SimpleFieldSet;
-import freenet.support.api.Bucket;
-
-/**
- * Event that signals that a plugin reply was received.
- */
-public class ReceivedReplyEvent {
-
- /** The connector that received the reply. */
- private final PluginConnector pluginConnector;
-
- /** The name of the plugin that sent the reply. */
- private final String pluginName;
-
- /** The identifier of the initial request. */
- private final String identifier;
-
- /** The fields containing the reply. */
- private final SimpleFieldSet fieldSet;
-
- /** The optional reply data. */
- private final Bucket data;
-
- /**
- * Creates a new “reply received” event.
- *
- * @param pluginConnector
- * The connector that received the event
- * @param pluginName
- * The name of the plugin that sent the reply
- * @param identifier
- * The identifier of the initial request
- * @param fieldSet
- * The fields containing the reply
- * @param data
- * The optional data of the reply
- */
- public ReceivedReplyEvent(PluginConnector pluginConnector, String pluginName, String identifier, SimpleFieldSet fieldSet, Bucket data) {
- this.pluginConnector = pluginConnector;
- this.pluginName = pluginName;
- this.identifier = identifier;
- this.fieldSet = fieldSet;
- this.data = data;
- }
-
- //
- // ACCESSORS
- //
-
- /**
- * Returns the plugin connector that received the reply.
- *
- * @return The plugin connector that received the reply
- */
- public PluginConnector pluginConnector() {
- return pluginConnector;
- }
-
- /**
- * Returns the name of the plugin that sent the reply.
- *
- * @return The name of the plugin that sent the reply
- */
- public String pluginName() {
- return pluginName;
- }
-
- /**
- * Returns the identifier of the initial request.
- *
- * @return The identifier of the initial request
- */
- public String identifier() {
- return identifier;
- }
-
- /**
- * Returns the fields containing the reply.
- *
- * @return The fields containing the reply
- */
- public SimpleFieldSet fieldSet() {
- return fieldSet;
- }
-
- /**
- * Returns the optional data of the reply.
- *
- * @return The optional data of the reply (may be {@code null})
- */
- public Bucket data() {
- return data;
- }
-
-}
+++ /dev/null
-/*
- * Sone - Context.java - Copyright © 2014–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import javax.annotation.Nullable;
-
-import com.google.common.base.Function;
-
-/**
- * Custom container for the Web of Trust context. This allows easier
- * configuration of dependency injection.
- */
-public class Context {
-
- public static final Function<Context, String> extractContext = new Function<Context, String>() {
- @Nullable
- @Override
- public String apply(@Nullable Context context) {
- return (context == null) ? null : context.getContext();
- }
- };
-
- private final String context;
-
- public Context(String context) {
- this.context = context;
- }
-
- public String getContext() {
- return context;
- }
-
-}
+++ /dev/null
-/*
- * Sone - DefaultIdentity.java - Copyright © 2010–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * A Web of Trust identity.
- */
-public class DefaultIdentity implements Identity {
-
- /** The ID of the identity. */
- private final String id;
-
- /** The nickname of the identity. */
- private final String nickname;
-
- /** The request URI of the identity. */
- private final String requestUri;
-
- /** The contexts of the identity. */
- private final Set<String> contexts = Collections.synchronizedSet(new HashSet<String>());
-
- /** The properties of the identity. */
- private final Map<String, String> properties = Collections.synchronizedMap(new HashMap<String, String>());
-
- /** Cached trust. */
- private final Map<OwnIdentity, Trust> trustCache = Collections.synchronizedMap(new HashMap<OwnIdentity, Trust>());
-
- /**
- * Creates a new identity.
- *
- * @param id
- * The ID of the identity
- * @param nickname
- * The nickname of the identity
- * @param requestUri
- * The request URI of the identity
- */
- public DefaultIdentity(String id, String nickname, String requestUri) {
- this.id = id;
- this.nickname = nickname;
- this.requestUri = requestUri;
- }
-
- //
- // ACCESSORS
- //
-
- @Override
- public String getId() {
- return id;
- }
-
- @Override
- public String getNickname() {
- return nickname;
- }
-
- @Override
- public String getRequestUri() {
- return requestUri;
- }
-
- @Override
- public Set<String> getContexts() {
- return Collections.unmodifiableSet(contexts);
- }
-
- @Override
- public boolean hasContext(String context) {
- return contexts.contains(context);
- }
-
- @Override
- public void setContexts(Collection<String> contexts) {
- this.contexts.clear();
- this.contexts.addAll(contexts);
- }
-
- @Override
- public Identity addContext(String context) {
- contexts.add(context);
- return this;
- }
-
- @Override
- public Identity removeContext(String context) {
- contexts.remove(context);
- return this;
- }
-
- @Override
- public Map<String, String> getProperties() {
- return Collections.unmodifiableMap(properties);
- }
-
- @Override
- public void setProperties(Map<String, String> properties) {
- this.properties.clear();
- this.properties.putAll(properties);
- }
-
- @Override
- public String getProperty(String name) {
- return properties.get(name);
- }
-
- @Override
- public Identity setProperty(String name, String value) {
- properties.put(name, value);
- return this;
- }
-
- @Override
- public Identity removeProperty(String name) {
- properties.remove(name);
- return this;
- }
-
- @Override
- public Trust getTrust(OwnIdentity ownIdentity) {
- return trustCache.get(ownIdentity);
- }
-
- @Override
- public Identity setTrust(OwnIdentity ownIdentity, Trust trust) {
- trustCache.put(ownIdentity, trust);
- return this;
- }
-
- @Override
- public Identity removeTrust(OwnIdentity ownIdentity) {
- trustCache.remove(ownIdentity);
- return this;
- }
-
- //
- // OBJECT METHODS
- //
-
- @Override
- public int hashCode() {
- return getId().hashCode();
- }
-
- @Override
- public boolean equals(Object object) {
- if (!(object instanceof Identity)) {
- return false;
- }
- Identity identity = (Identity) object;
- return identity.getId().equals(getId());
- }
-
- @Override
- public String toString() {
- return getClass().getSimpleName() + "[id=" + id + ",nickname=" + nickname + ",contexts=" + contexts + ",properties=" + properties + "]";
- }
-
-}
+++ /dev/null
-/*
- * Sone - DefaultOwnIdentity.java - Copyright © 2010–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import static com.google.common.base.Preconditions.checkNotNull;
-
-/**
- * An own identity is an identity that the owner of the node has full control
- * over.
- */
-public class DefaultOwnIdentity extends DefaultIdentity implements OwnIdentity {
-
- /** The insert URI of the identity. */
- private final String insertUri;
-
- /**
- * Creates a new own identity.
- *
- * @param id
- * The ID of the identity
- * @param nickname
- * The nickname of the identity
- * @param requestUri
- * The request URI of the identity
- * @param insertUri
- * The insert URI of the identity
- */
- public DefaultOwnIdentity(String id, String nickname, String requestUri, String insertUri) {
- super(id, nickname, requestUri);
- this.insertUri = checkNotNull(insertUri);
- }
-
- //
- // ACCESSORS
- //
-
- @Override
- public String getInsertUri() {
- return insertUri;
- }
-
- @Override
- public OwnIdentity addContext(String context) {
- return (OwnIdentity) super.addContext(context);
- }
-
- @Override
- public OwnIdentity removeContext(String context) {
- return (OwnIdentity) super.removeContext(context);
- }
-
- @Override
- public OwnIdentity setProperty(String name, String value) {
- return (OwnIdentity) super.setProperty(name, value);
- }
-
- @Override
- public OwnIdentity removeProperty(String name) {
- return (OwnIdentity) super.removeProperty(name);
- }
-
- //
- // OBJECT METHODS
- //
-
- @Override
- public int hashCode() {
- return super.hashCode();
- }
-
- @Override
- public boolean equals(Object object) {
- return super.equals(object);
- }
-
-}
*/
public interface Identity {
- public static final Function<Identity, Set<String>> TO_CONTEXTS = new Function<Identity, Set<String>>() {
- @Override
- public Set<String> apply(Identity identity) {
- return (identity == null) ? Collections.<String>emptySet() : identity.getContexts();
- }
- };
-
- public static final Function<Identity, Map<String, String>> TO_PROPERTIES = new Function<Identity, Map<String, String>>() {
- @Override
- public Map<String, String> apply(Identity input) {
- return (input == null) ? Collections.<String, String>emptyMap() : input.getProperties();
- }
- };
-
/**
* Returns the ID of the identity.
*
* @param contexts
* All contexts of the identity
*/
- public void setContexts(Collection<String> contexts);
+ public void setContexts(Set<String> contexts);
/**
* Removes the given context from this identity.
+++ /dev/null
-/*
- * Sone - IdentityChangeDetector.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import static com.google.common.base.Optional.absent;
-import static com.google.common.base.Optional.fromNullable;
-import static com.google.common.base.Predicates.not;
-import static com.google.common.collect.FluentIterable.from;
-import static net.pterodactylus.sone.freenet.wot.Identity.TO_CONTEXTS;
-import static net.pterodactylus.sone.freenet.wot.Identity.TO_PROPERTIES;
-
-import java.util.Collection;
-import java.util.Map;
-import java.util.Map.Entry;
-
-import com.google.common.base.Optional;
-import com.google.common.base.Predicate;
-import com.google.common.collect.FluentIterable;
-import com.google.common.collect.ImmutableMap;
-
-/**
- * Detects changes between two lists of {@link Identity}s. The detector can find
- * added and removed identities, and for identities that exist in both list
- * their contexts and properties are checked for added, removed, or (in case of
- * properties) changed values.
- */
-public class IdentityChangeDetector {
-
- private final Map<String, Identity> oldIdentities;
- private Optional<IdentityProcessor> onNewIdentity = absent();
- private Optional<IdentityProcessor> onRemovedIdentity = absent();
- private Optional<IdentityProcessor> onChangedIdentity = absent();
- private Optional<IdentityProcessor> onUnchangedIdentity = absent();
-
- public IdentityChangeDetector(Collection<? extends Identity> oldIdentities) {
- this.oldIdentities = convertToMap(oldIdentities);
- }
-
- public void onNewIdentity(IdentityProcessor onNewIdentity) {
- this.onNewIdentity = fromNullable(onNewIdentity);
- }
-
- public void onRemovedIdentity(IdentityProcessor onRemovedIdentity) {
- this.onRemovedIdentity = fromNullable(onRemovedIdentity);
- }
-
- public void onChangedIdentity(IdentityProcessor onChangedIdentity) {
- this.onChangedIdentity = fromNullable(onChangedIdentity);
- }
-
- public void onUnchangedIdentity(IdentityProcessor onUnchangedIdentity) {
- this.onUnchangedIdentity = fromNullable(onUnchangedIdentity);
- }
-
- public void detectChanges(final Collection<? extends Identity> newIdentities) {
- notifyForRemovedIdentities(from(oldIdentities.values()).filter(notContainedIn(newIdentities)));
- notifyForNewIdentities(from(newIdentities).filter(notContainedIn(oldIdentities.values())));
- notifyForChangedIdentities(from(newIdentities).filter(containedIn(oldIdentities)).filter(hasChanged(oldIdentities)));
- notifyForUnchangedIdentities(from(newIdentities).filter(containedIn(oldIdentities)).filter(not(hasChanged(oldIdentities))));
- }
-
- private void notifyForRemovedIdentities(Iterable<Identity> identities) {
- notify(onRemovedIdentity, identities);
- }
-
- private void notifyForNewIdentities(FluentIterable<? extends Identity> newIdentities) {
- notify(onNewIdentity, newIdentities);
- }
-
- private void notifyForChangedIdentities(FluentIterable<? extends Identity> identities) {
- notify(onChangedIdentity, identities);
- }
-
- private void notifyForUnchangedIdentities(FluentIterable<? extends Identity> identities) {
- notify(onUnchangedIdentity, identities);
- }
-
- private void notify(Optional<IdentityProcessor> identityProcessor, Iterable<? extends Identity> identities) {
- if (!identityProcessor.isPresent()) {
- return;
- }
- for (Identity identity : identities) {
- identityProcessor.get().processIdentity(identity);
- }
- }
-
- private static Predicate<Identity> hasChanged(final Map<String, Identity> oldIdentities) {
- return new Predicate<Identity>() {
- @Override
- public boolean apply(Identity identity) {
- return (identity != null) && identityHasChanged(oldIdentities.get(identity.getId()), identity);
- }
- };
- }
-
- private static boolean identityHasChanged(Identity oldIdentity, Identity newIdentity) {
- return identityHasNewContexts(oldIdentity, newIdentity)
- || identityHasRemovedContexts(oldIdentity, newIdentity)
- || identityHasNewProperties(oldIdentity, newIdentity)
- || identityHasRemovedProperties(oldIdentity, newIdentity)
- || identityHasChangedProperties(oldIdentity, newIdentity);
- }
-
- private static boolean identityHasNewContexts(Identity oldIdentity, Identity newIdentity) {
- return from(TO_CONTEXTS.apply(newIdentity)).anyMatch(notAContextOf(oldIdentity));
- }
-
- private static boolean identityHasRemovedContexts(Identity oldIdentity, Identity newIdentity) {
- return from(TO_CONTEXTS.apply(oldIdentity)).anyMatch(notAContextOf(newIdentity));
- }
-
- private static boolean identityHasNewProperties(Identity oldIdentity, Identity newIdentity) {
- return from(TO_PROPERTIES.apply(newIdentity).entrySet()).anyMatch(notAPropertyOf(oldIdentity));
- }
-
- private static boolean identityHasRemovedProperties(Identity oldIdentity, Identity newIdentity) {
- return from(TO_PROPERTIES.apply(oldIdentity).entrySet()).anyMatch(notAPropertyOf(newIdentity));
- }
-
- private static boolean identityHasChangedProperties(Identity oldIdentity, Identity newIdentity) {
- return from(TO_PROPERTIES.apply(oldIdentity).entrySet()).anyMatch(hasADifferentValueThanIn(newIdentity));
- }
-
- private static Predicate<Identity> containedIn(final Map<String, Identity> identities) {
- return new Predicate<Identity>() {
- @Override
- public boolean apply(Identity identity) {
- return (identity != null) && identities.containsKey(identity.getId());
- }
- };
- }
-
- private static Predicate<String> notAContextOf(final Identity identity) {
- return new Predicate<String>() {
- @Override
- public boolean apply(String context) {
- return (identity != null) && !identity.getContexts().contains(context);
- }
- };
- }
-
- private static Predicate<Identity> notContainedIn(final Collection<? extends Identity> newIdentities) {
- return new Predicate<Identity>() {
- @Override
- public boolean apply(Identity identity) {
- return (identity != null) && !newIdentities.contains(identity);
- }
- };
- }
-
- private static Predicate<Entry<String, String>> notAPropertyOf(final Identity identity) {
- return new Predicate<Entry<String, String>>() {
- @Override
- public boolean apply(Entry<String, String> property) {
- return (property != null) && !identity.getProperties().containsKey(property.getKey());
- }
- };
- }
-
- private static Predicate<Entry<String, String>> hasADifferentValueThanIn(final Identity newIdentity) {
- return new Predicate<Entry<String, String>>() {
- @Override
- public boolean apply(Entry<String, String> property) {
- return (property != null) && !newIdentity.getProperty(property.getKey()).equals(property.getValue());
- }
- };
- }
-
- private static Map<String, Identity> convertToMap(Collection<? extends Identity> identities) {
- ImmutableMap.Builder<String, Identity> mapBuilder = ImmutableMap.builder();
- for (Identity identity : identities) {
- mapBuilder.put(identity.getId(), identity);
- }
- return mapBuilder.build();
- }
-
- public interface IdentityProcessor {
-
- void processIdentity(Identity identity);
-
- }
-
-}
--- /dev/null
+/*
+ * Sone - IdentityChangeDetector.java - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+/**
+ * Detects changes between two lists of [Identity]s. The detector can find
+ * added and removed identities, and for identities that exist in both list
+ * their contexts and properties are checked for added, removed, or (in case of
+ * properties) changed values.
+ */
+class IdentityChangeDetector(oldIdentities: Collection<Identity>) {
+
+ private val oldIdentities: Map<String, Identity> = oldIdentities.associateBy { it.id }
+ var onNewIdentity: IdentityProcessor? = null
+ var onRemovedIdentity: IdentityProcessor? = null
+ var onChangedIdentity: IdentityProcessor? = null
+ var onUnchangedIdentity: IdentityProcessor? = null
+
+ fun detectChanges(newIdentities: Collection<Identity>) {
+ onRemovedIdentity.notify(oldIdentities.values.filter { it !in newIdentities })
+ onNewIdentity.notify(newIdentities.filter { it !in oldIdentities.values })
+ onChangedIdentity.notify(newIdentities.filter { it.id in oldIdentities }.filter { identityHasChanged(oldIdentities[it.id]!!, it) })
+ onUnchangedIdentity.notify(newIdentities.filter { it.id in oldIdentities }.filterNot { identityHasChanged(oldIdentities[it.id]!!, it) })
+ }
+
+ private fun identityHasChanged(oldIdentity: Identity, newIdentity: Identity?) =
+ identityHasNewContexts(oldIdentity, newIdentity!!)
+ || identityHasRemovedContexts(oldIdentity, newIdentity)
+ || identityHasNewProperties(oldIdentity, newIdentity)
+ || identityHasRemovedProperties(oldIdentity, newIdentity)
+ || identityHasChangedProperties(oldIdentity, newIdentity)
+
+ private fun identityHasNewContexts(oldIdentity: Identity, newIdentity: Identity) =
+ newIdentity.contexts.any { it !in oldIdentity.contexts }
+
+ private fun identityHasRemovedContexts(oldIdentity: Identity, newIdentity: Identity) =
+ oldIdentity.contexts.any { it !in newIdentity.contexts }
+
+ private fun identityHasNewProperties(oldIdentity: Identity, newIdentity: Identity) =
+ newIdentity.properties.keys.any { it !in oldIdentity.properties }
+
+ private fun identityHasRemovedProperties(oldIdentity: Identity, newIdentity: Identity) =
+ oldIdentity.properties.keys.any { it !in newIdentity.properties }
+
+ private fun identityHasChangedProperties(oldIdentity: Identity, newIdentity: Identity) =
+ oldIdentity.properties.entries.any { newIdentity.properties[it.key] != it.value }
+
+}
+
+typealias IdentityProcessor = (Identity) -> Unit
+
+private fun IdentityProcessor?.notify(identities: Iterable<Identity>) =
+ this?.let { identities.forEach(this::invoke) }
+++ /dev/null
-/*
- * Sone - IdentityChangeEventSender.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import java.util.Collection;
-import java.util.Map;
-
-import net.pterodactylus.sone.freenet.wot.IdentityChangeDetector.IdentityProcessor;
-import net.pterodactylus.sone.freenet.wot.event.IdentityAddedEvent;
-import net.pterodactylus.sone.freenet.wot.event.IdentityRemovedEvent;
-import net.pterodactylus.sone.freenet.wot.event.IdentityUpdatedEvent;
-import net.pterodactylus.sone.freenet.wot.event.OwnIdentityAddedEvent;
-import net.pterodactylus.sone.freenet.wot.event.OwnIdentityRemovedEvent;
-
-import com.google.common.eventbus.EventBus;
-
-/**
- * Detects changes in {@link Identity}s trusted my multiple {@link
- * OwnIdentity}s.
- *
- * @see IdentityChangeDetector
- */
-public class IdentityChangeEventSender {
-
- private final EventBus eventBus;
- private final Map<OwnIdentity, Collection<Identity>> oldIdentities;
-
- public IdentityChangeEventSender(EventBus eventBus, Map<OwnIdentity, Collection<Identity>> oldIdentities) {
- this.eventBus = eventBus;
- this.oldIdentities = oldIdentities;
- }
-
- public void detectChanges(Map<OwnIdentity, Collection<Identity>> identities) {
- IdentityChangeDetector identityChangeDetector = new IdentityChangeDetector(oldIdentities.keySet());
- identityChangeDetector.onNewIdentity(addNewOwnIdentityAndItsTrustedIdentities(identities));
- identityChangeDetector.onRemovedIdentity(removeOwnIdentityAndItsTrustedIdentities(oldIdentities));
- identityChangeDetector.onUnchangedIdentity(detectChangesInTrustedIdentities(identities, oldIdentities));
- identityChangeDetector.detectChanges(identities.keySet());
- }
-
- private IdentityProcessor addNewOwnIdentityAndItsTrustedIdentities(final Map<OwnIdentity, Collection<Identity>> newIdentities) {
- return new IdentityProcessor() {
- @Override
- public void processIdentity(Identity identity) {
- eventBus.post(new OwnIdentityAddedEvent((OwnIdentity) identity));
- for (Identity newIdentity : newIdentities.get((OwnIdentity) identity)) {
- eventBus.post(new IdentityAddedEvent((OwnIdentity) identity, newIdentity));
- }
- }
- };
- }
-
- private IdentityProcessor removeOwnIdentityAndItsTrustedIdentities(final Map<OwnIdentity, Collection<Identity>> oldIdentities) {
- return new IdentityProcessor() {
- @Override
- public void processIdentity(Identity identity) {
- eventBus.post(new OwnIdentityRemovedEvent((OwnIdentity) identity));
- for (Identity removedIdentity : oldIdentities.get((OwnIdentity) identity)) {
- eventBus.post(new IdentityRemovedEvent((OwnIdentity) identity, removedIdentity));
- }
- }
- };
- }
-
- private IdentityProcessor detectChangesInTrustedIdentities(Map<OwnIdentity, Collection<Identity>> newIdentities, Map<OwnIdentity, Collection<Identity>> oldIdentities) {
- return new DefaultIdentityProcessor(oldIdentities, newIdentities);
- }
-
- private class DefaultIdentityProcessor implements IdentityProcessor {
-
- private final Map<OwnIdentity, Collection<Identity>> oldIdentities;
- private final Map<OwnIdentity, Collection<Identity>> newIdentities;
-
- public DefaultIdentityProcessor(Map<OwnIdentity, Collection<Identity>> oldIdentities, Map<OwnIdentity, Collection<Identity>> newIdentities) {
- this.oldIdentities = oldIdentities;
- this.newIdentities = newIdentities;
- }
-
- @Override
- public void processIdentity(Identity ownIdentity) {
- IdentityChangeDetector identityChangeDetector = new IdentityChangeDetector(oldIdentities.get((OwnIdentity) ownIdentity));
- identityChangeDetector.onNewIdentity(notifyForAddedIdentities((OwnIdentity) ownIdentity));
- identityChangeDetector.onRemovedIdentity(notifyForRemovedIdentities((OwnIdentity) ownIdentity));
- identityChangeDetector.onChangedIdentity(notifyForChangedIdentities((OwnIdentity) ownIdentity));
- identityChangeDetector.detectChanges(newIdentities.get((OwnIdentity) ownIdentity));
- }
-
- private IdentityProcessor notifyForChangedIdentities(final OwnIdentity ownIdentity) {
- return new IdentityProcessor() {
- @Override
- public void processIdentity(Identity identity) {
- eventBus.post(new IdentityUpdatedEvent(ownIdentity, identity));
- }
- };
- }
-
- private IdentityProcessor notifyForRemovedIdentities(final OwnIdentity ownIdentity) {
- return new IdentityProcessor() {
- @Override
- public void processIdentity(Identity identity) {
- eventBus.post(new IdentityRemovedEvent(ownIdentity, identity));
- }
- };
- }
-
- private IdentityProcessor notifyForAddedIdentities(final OwnIdentity ownIdentity) {
- return new IdentityProcessor() {
- @Override
- public void processIdentity(Identity identity) {
- eventBus.post(new IdentityAddedEvent(ownIdentity, identity));
- }
- };
- }
-
- }
-
-}
+++ /dev/null
-/*
- * Sone - IdentityLoader.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import static java.util.concurrent.TimeUnit.*;
-import static net.pterodactylus.sone.freenet.wot.Context.*;
-
-import java.util.*;
-import java.util.logging.*;
-
-import net.pterodactylus.sone.freenet.plugin.*;
-
-import com.google.common.base.Optional;
-import com.google.common.base.*;
-import com.google.inject.*;
-
-/**
- * Loads {@link OwnIdentity}s and the {@link Identity}s they trust.
- */
-public class IdentityLoader {
-
- private final Logger logger = Logger.getLogger(IdentityLoader.class.getName());
- private final WebOfTrustConnector webOfTrustConnector;
- private final Optional<Context> context;
-
- public IdentityLoader(WebOfTrustConnector webOfTrustConnector) {
- this(webOfTrustConnector, Optional.<Context>absent());
- }
-
- @Inject
- public IdentityLoader(WebOfTrustConnector webOfTrustConnector, Optional<Context> context) {
- this.webOfTrustConnector = webOfTrustConnector;
- this.context = context;
- }
-
- public Map<OwnIdentity, Collection<Identity>> loadIdentities() throws WebOfTrustException {
- Stopwatch stopwatch = Stopwatch.createStarted();
- Collection<OwnIdentity> currentOwnIdentities = webOfTrustConnector.loadAllOwnIdentities();
- logger.fine("Loaded " + currentOwnIdentities.size() + " own identities in " + (stopwatch.elapsed(MILLISECONDS) / 1000.0) + "s.");
- return loadTrustedIdentitiesForOwnIdentities(currentOwnIdentities);
- }
-
- private Map<OwnIdentity, Collection<Identity>> loadTrustedIdentitiesForOwnIdentities(Collection<OwnIdentity> ownIdentities) throws PluginException {
- Map<OwnIdentity, Collection<Identity>> currentIdentities = new HashMap<>();
-
- for (OwnIdentity ownIdentity : ownIdentities) {
- if (identityDoesNotHaveTheCorrectContext(ownIdentity)) {
- currentIdentities.put(ownIdentity, Collections.<Identity>emptySet());
- continue;
- }
-
- Stopwatch stopwatch = Stopwatch.createStarted();
- Set<Identity> trustedIdentities = webOfTrustConnector.loadTrustedIdentities(ownIdentity, context.transform(extractContext));
- logger.fine("Loaded " + trustedIdentities.size() + " identities for " + ownIdentity.getNickname() + " in " + (stopwatch.elapsed(MILLISECONDS) / 1000.0) + "s.");
- currentIdentities.put(ownIdentity, trustedIdentities);
- }
-
- return currentIdentities;
- }
-
- private boolean identityDoesNotHaveTheCorrectContext(OwnIdentity ownIdentity) {
- return context.isPresent() && !ownIdentity.hasContext(context.transform(extractContext).get());
- }
-
-}
+++ /dev/null
-package net.pterodactylus.sone.freenet.wot;
-
-import java.util.Set;
-
-import net.pterodactylus.util.service.Service;
-
-import com.google.common.eventbus.EventBus;
-import com.google.inject.ImplementedBy;
-
-/**
- * Connects to a {@link WebOfTrustConnector} and sends identity events to an
- * {@link EventBus}.
- */
-@ImplementedBy(IdentityManagerImpl.class)
-public interface IdentityManager extends Service {
-
- boolean isConnected();
- Set<OwnIdentity> getAllOwnIdentities();
-
-}
+++ /dev/null
-/*
- * Sone - IdentityManagerImpl.java - Copyright © 2010–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import static java.util.logging.Logger.getLogger;
-
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import net.pterodactylus.sone.freenet.plugin.PluginException;
-import net.pterodactylus.util.service.AbstractService;
-
-import com.google.common.collect.Sets;
-import com.google.common.eventbus.EventBus;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-/**
- * The identity manager takes care of loading and storing identities, their
- * contexts, and properties. It does so in a way that does not expose errors via
- * exceptions but it only logs them and tries to return sensible defaults.
- * <p>
- * It is also responsible for polling identities from the Web of Trust plugin
- * and sending events to the {@link EventBus} when {@link Identity}s and
- * {@link OwnIdentity}s are discovered or disappearing.
- */
-@Singleton
-public class IdentityManagerImpl extends AbstractService implements IdentityManager {
-
- /** The logger. */
- private static final Logger logger = getLogger(IdentityManagerImpl.class.getName());
-
- /** The event bus. */
- private final EventBus eventBus;
-
- private final IdentityLoader identityLoader;
-
- /** The Web of Trust connector. */
- private final WebOfTrustConnector webOfTrustConnector;
-
- /** The currently known own identities. */
- private final Set<OwnIdentity> currentOwnIdentities = Sets.newHashSet();
-
- /**
- * Creates a new identity manager.
- *
- * @param eventBus
- * The event bus
- * @param webOfTrustConnector
- * The Web of Trust connector
- */
- @Inject
- public IdentityManagerImpl(EventBus eventBus, WebOfTrustConnector webOfTrustConnector, IdentityLoader identityLoader) {
- super("Sone Identity Manager", false);
- this.eventBus = eventBus;
- this.webOfTrustConnector = webOfTrustConnector;
- this.identityLoader = identityLoader;
- }
-
- //
- // ACCESSORS
- //
-
- /**
- * Returns whether the Web of Trust plugin could be reached during the last
- * try.
- *
- * @return {@code true} if the Web of Trust plugin is connected,
- * {@code false} otherwise
- */
- @Override
- public boolean isConnected() {
- try {
- webOfTrustConnector.ping();
- return true;
- } catch (PluginException pe1) {
- /* not connected, ignore. */
- return false;
- }
- }
-
- /**
- * Returns all own identities.
- *
- * @return All own identities
- */
- @Override
- public Set<OwnIdentity> getAllOwnIdentities() {
- synchronized (currentOwnIdentities) {
- return new HashSet<>(currentOwnIdentities);
- }
- }
-
- //
- // SERVICE METHODS
- //
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void serviceRun() {
- Map<OwnIdentity, Collection<Identity>> oldIdentities = new HashMap<>();
-
- while (!shouldStop()) {
- try {
- Map<OwnIdentity, Collection<Identity>> currentIdentities = identityLoader.loadIdentities();
-
- IdentityChangeEventSender identityChangeEventSender = new IdentityChangeEventSender(eventBus, oldIdentities);
- identityChangeEventSender.detectChanges(currentIdentities);
-
- oldIdentities = currentIdentities;
-
- synchronized (currentOwnIdentities) {
- currentOwnIdentities.clear();
- currentOwnIdentities.addAll(currentIdentities.keySet());
- }
- } catch (WebOfTrustException wote1) {
- logger.log(Level.WARNING, "WoT has disappeared!", wote1);
- }
-
- /* wait a minute before checking again. */
- sleep(60 * 1000);
- }
- }
-
-}
+++ /dev/null
-/*
- * Sone - Trust.java - Copyright © 2010–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import static com.google.common.base.Objects.equal;
-
-import com.google.common.base.Objects;
-
-/**
- * Container class for trust in the web of trust.
- */
-public class Trust {
-
- /** Explicitely assigned trust. */
- private final Integer explicit;
-
- /** Implicitely calculated trust. */
- private final Integer implicit;
-
- /** The distance from the owner of the trust tree. */
- private final Integer distance;
-
- /**
- * Creates a new trust container.
- *
- * @param explicit
- * The explicit trust
- * @param implicit
- * The implicit trust
- * @param distance
- * The distance
- */
- public Trust(Integer explicit, Integer implicit, Integer distance) {
- this.explicit = explicit;
- this.implicit = implicit;
- this.distance = distance;
- }
-
- /**
- * Returns the trust explicitely assigned to an identity.
- *
- * @return The explicitely assigned trust, or {@code null} if the identity
- * is not in the own identity’s trust tree
- */
- public Integer getExplicit() {
- return explicit;
- }
-
- /**
- * Returns the implicitely assigned trust, or the calculated trust.
- *
- * @return The calculated trust, or {@code null} if the identity is not in
- * the own identity’s trust tree
- */
- public Integer getImplicit() {
- return implicit;
- }
-
- /**
- * Returns the distance of the trusted identity from the trusting identity.
- *
- * @return The distance from the own identity, or {@code null} if the
- * identity is not in the own identity’s trust tree
- */
- public Integer getDistance() {
- return distance;
- }
-
- @Override
- public boolean equals(Object object) {
- if (!(object instanceof Trust)) {
- return false;
- }
- Trust trust = (Trust) object;
- return equal(getExplicit(), trust.getExplicit()) && equal(getImplicit(), trust.getImplicit()) && equal(getDistance(), trust.getDistance());
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(explicit, implicit, distance);
- }
-
- /** {@inheritDoc} */
- @Override
- public String toString() {
- return getClass().getName() + "[explicit=" + explicit + ",implicit=" + implicit + ",distance=" + distance + "]";
- }
-
-}
+++ /dev/null
-/*
- * Sone - WebOfTrustConnector.java - Copyright © 2010–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import static java.util.logging.Logger.getLogger;
-import static net.pterodactylus.sone.utils.NumberParsers.parseInt;
-
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-import java.util.concurrent.atomic.AtomicLong;
-import java.util.logging.Level;
-import java.util.logging.Logger;
-
-import net.pterodactylus.sone.freenet.plugin.PluginConnector;
-import net.pterodactylus.sone.freenet.plugin.PluginException;
-import net.pterodactylus.sone.freenet.plugin.event.ReceivedReplyEvent;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.MapMaker;
-import com.google.common.eventbus.Subscribe;
-import com.google.inject.Inject;
-import com.google.inject.Singleton;
-
-import freenet.support.SimpleFieldSet;
-import freenet.support.api.Bucket;
-
-/**
- * Connector for the Web of Trust plugin.
- */
-@Singleton
-public class WebOfTrustConnector {
-
- /** The logger. */
- private static final Logger logger = getLogger(WebOfTrustConnector.class.getName());
-
- /** The name of the WoT plugin. */
- private static final String WOT_PLUGIN_NAME = "plugins.WebOfTrust.WebOfTrust";
-
- /** Counter for connection identifiers. */
- private final AtomicLong counter = new AtomicLong();
-
- /** The plugin connector. */
- private final PluginConnector pluginConnector;
-
- /** Map for replies. */
- private final Map<PluginIdentifier, Reply> replies = new MapMaker().makeMap();
-
- /**
- * Creates a new Web of Trust connector that uses the given plugin
- * connector.
- *
- * @param pluginConnector
- * The plugin connector
- */
- @Inject
- public WebOfTrustConnector(PluginConnector pluginConnector) {
- this.pluginConnector = pluginConnector;
- }
-
- //
- // ACTIONS
- //
-
- /**
- * Stops the web of trust connector.
- */
- public void stop() {
- /* does nothing. */
- }
-
- /**
- * Loads all own identities from the Web of Trust plugin.
- *
- * @return All own identity
- * @throws WebOfTrustException
- * if the own identities can not be loaded
- */
- public Set<OwnIdentity> loadAllOwnIdentities() throws WebOfTrustException {
- Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetOwnIdentities").get());
- SimpleFieldSet fields = reply.getFields();
- int ownIdentityCounter = -1;
- Set<OwnIdentity> ownIdentities = new HashSet<>();
- while (true) {
- String id = fields.get("Identity" + ++ownIdentityCounter);
- if (id == null) {
- break;
- }
- String requestUri = fields.get("RequestURI" + ownIdentityCounter);
- String insertUri = fields.get("InsertURI" + ownIdentityCounter);
- String nickname = fields.get("Nickname" + ownIdentityCounter);
- DefaultOwnIdentity ownIdentity = new DefaultOwnIdentity(id, nickname, requestUri, insertUri);
- ownIdentity.setContexts(parseContexts("Contexts" + ownIdentityCounter + ".", fields));
- ownIdentity.setProperties(parseProperties("Properties" + ownIdentityCounter + ".", fields));
- ownIdentities.add(ownIdentity);
- }
- return ownIdentities;
- }
-
- /**
- * Loads all identities that the given identities trusts with a score of
- * more than 0.
- *
- * @param ownIdentity
- * The own identity
- * @return All trusted identities
- * @throws PluginException
- * if an error occured talking to the Web of Trust plugin
- */
- public Set<Identity> loadTrustedIdentities(OwnIdentity ownIdentity) throws PluginException {
- return loadTrustedIdentities(ownIdentity, Optional.<String>absent());
- }
-
- /**
- * Loads all identities that the given identities trusts with a score of
- * more than 0 and the (optional) given context.
- *
- * @param ownIdentity
- * The own identity
- * @param context
- * The context to filter, or {@code null}
- * @return All trusted identities
- * @throws PluginException
- * if an error occured talking to the Web of Trust plugin
- */
- public Set<Identity> loadTrustedIdentities(OwnIdentity ownIdentity, Optional<String> context) throws PluginException {
- Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentitiesByScore").put("Truster", ownIdentity.getId()).put("Selection", "+").put("Context", context.or("")).put("WantTrustValues", "true").get());
- SimpleFieldSet fields = reply.getFields();
- Set<Identity> identities = new HashSet<>();
- int identityCounter = -1;
- while (true) {
- String id = fields.get("Identity" + ++identityCounter);
- if (id == null) {
- break;
- }
- String nickname = fields.get("Nickname" + identityCounter);
- String requestUri = fields.get("RequestURI" + identityCounter);
- DefaultIdentity identity = new DefaultIdentity(id, nickname, requestUri);
- identity.setContexts(parseContexts("Contexts" + identityCounter + ".", fields));
- identity.setProperties(parseProperties("Properties" + identityCounter + ".", fields));
- Integer trust = parseInt(fields.get("Trust" + identityCounter), null);
- int score = parseInt(fields.get("Score" + identityCounter), 0);
- int rank = parseInt(fields.get("Rank" + identityCounter), 0);
- identity.setTrust(ownIdentity, new Trust(trust, score, rank));
- identities.add(identity);
- }
- return identities;
- }
-
- /**
- * Adds the given context to the given identity.
- *
- * @param ownIdentity
- * The identity to add the context to
- * @param context
- * The context to add
- * @throws PluginException
- * if an error occured talking to the Web of Trust plugin
- */
- public void addContext(OwnIdentity ownIdentity, String context) throws PluginException {
- performRequest(SimpleFieldSetConstructor.create().put("Message", "AddContext").put("Identity", ownIdentity.getId()).put("Context", context).get());
- }
-
- /**
- * Removes the given context from the given identity.
- *
- * @param ownIdentity
- * The identity to remove the context from
- * @param context
- * The context to remove
- * @throws PluginException
- * if an error occured talking to the Web of Trust plugin
- */
- public void removeContext(OwnIdentity ownIdentity, String context) throws PluginException {
- performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveContext").put("Identity", ownIdentity.getId()).put("Context", context).get());
- }
-
- /**
- * Returns the value of the property with the given name.
- *
- * @param identity
- * The identity whose properties to check
- * @param name
- * The name of the property to return
- * @return The value of the property, or {@code null} if there is no value
- * @throws PluginException
- * if an error occured talking to the Web of Trust plugin
- */
- public String getProperty(Identity identity, String name) throws PluginException {
- Reply reply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetProperty").put("Identity", identity.getId()).put("Property", name).get());
- return reply.getFields().get("Property");
- }
-
- /**
- * Sets the property with the given name to the given value.
- *
- * @param ownIdentity
- * The identity to set the property on
- * @param name
- * The name of the property to set
- * @param value
- * The value to set
- * @throws PluginException
- * if an error occured talking to the Web of Trust plugin
- */
- public void setProperty(OwnIdentity ownIdentity, String name, String value) throws PluginException {
- performRequest(SimpleFieldSetConstructor.create().put("Message", "SetProperty").put("Identity", ownIdentity.getId()).put("Property", name).put("Value", value).get());
- }
-
- /**
- * Removes the property with the given name.
- *
- * @param ownIdentity
- * The identity to remove the property from
- * @param name
- * The name of the property to remove
- * @throws PluginException
- * if an error occured talking to the Web of Trust plugin
- */
- public void removeProperty(OwnIdentity ownIdentity, String name) throws PluginException {
- performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveProperty").put("Identity", ownIdentity.getId()).put("Property", name).get());
- }
-
- /**
- * Returns the trust for the given identity assigned to it by the given own
- * identity.
- *
- * @param ownIdentity
- * The own identity
- * @param identity
- * The identity to get the trust for
- * @return The trust for the given identity
- * @throws PluginException
- * if an error occured talking to the Web of Trust plugin
- */
- public Trust getTrust(OwnIdentity ownIdentity, Identity identity) throws PluginException {
- Reply getTrustReply = performRequest(SimpleFieldSetConstructor.create().put("Message", "GetIdentity").put("Truster", ownIdentity.getId()).put("Identity", identity.getId()).get());
- String trust = getTrustReply.getFields().get("Trust");
- String score = getTrustReply.getFields().get("Score");
- String rank = getTrustReply.getFields().get("Rank");
- Integer explicit = null;
- Integer implicit = null;
- Integer distance = null;
- try {
- explicit = Integer.valueOf(trust);
- } catch (NumberFormatException nfe1) {
- /* ignore. */
- }
- try {
- implicit = Integer.valueOf(score);
- distance = Integer.valueOf(rank);
- } catch (NumberFormatException nfe1) {
- /* ignore. */
- }
- return new Trust(explicit, implicit, distance);
- }
-
- /**
- * Sets the trust for the given identity.
- *
- * @param ownIdentity
- * The trusting identity
- * @param identity
- * The trusted identity
- * @param trust
- * The amount of trust (-100 thru 100)
- * @param comment
- * The comment or explanation of the trust value
- * @throws PluginException
- * if an error occured talking to the Web of Trust plugin
- */
- public void setTrust(OwnIdentity ownIdentity, Identity identity, int trust, String comment) throws PluginException {
- performRequest(SimpleFieldSetConstructor.create().put("Message", "SetTrust").put("Truster", ownIdentity.getId()).put("Trustee", identity.getId()).put("Value", String.valueOf(trust)).put("Comment", comment).get());
- }
-
- /**
- * Removes any trust assignment of the given own identity for the given
- * identity.
- *
- * @param ownIdentity
- * The own identity
- * @param identity
- * The identity to remove all trust for
- * @throws WebOfTrustException
- * if an error occurs
- */
- public void removeTrust(OwnIdentity ownIdentity, Identity identity) throws WebOfTrustException {
- performRequest(SimpleFieldSetConstructor.create().put("Message", "RemoveTrust").put("Truster", ownIdentity.getId()).put("Trustee", identity.getId()).get());
- }
-
- /**
- * Pings the Web of Trust plugin. If the plugin can not be reached, a
- * {@link PluginException} is thrown.
- *
- * @throws PluginException
- * if the plugin is not loaded
- */
- public void ping() throws PluginException {
- performRequest(SimpleFieldSetConstructor.create().put("Message", "Ping").get());
- }
-
- //
- // PRIVATE ACTIONS
- //
-
- /**
- * Parses the contexts from the given fields.
- *
- * @param prefix
- * The prefix to use to access the contexts
- * @param fields
- * The fields to parse the contexts from
- * @return The parsed contexts
- */
- private static Set<String> parseContexts(String prefix, SimpleFieldSet fields) {
- Set<String> contexts = new HashSet<>();
- int contextCounter = -1;
- while (true) {
- String context = fields.get(prefix + "Context" + ++contextCounter);
- if (context == null) {
- break;
- }
- contexts.add(context);
- }
- return contexts;
- }
-
- /**
- * Parses the properties from the given fields.
- *
- * @param prefix
- * The prefix to use to access the properties
- * @param fields
- * The fields to parse the properties from
- * @return The parsed properties
- */
- private static Map<String, String> parseProperties(String prefix, SimpleFieldSet fields) {
- Map<String, String> properties = new HashMap<>();
- int propertiesCounter = -1;
- while (true) {
- String propertyName = fields.get(prefix + "Property" + ++propertiesCounter + ".Name");
- if (propertyName == null) {
- break;
- }
- String propertyValue = fields.get(prefix + "Property" + propertiesCounter + ".Value");
- properties.put(propertyName, propertyValue);
- }
- return properties;
- }
-
- /**
- * Sends a request containing the given fields and waits for the target
- * message.
- *
- * @param fields
- * The fields of the message
- * @return The reply message
- * @throws PluginException
- * if the request could not be sent
- */
- private Reply performRequest(SimpleFieldSet fields) throws PluginException {
- return performRequest(fields, null);
- }
-
- /**
- * Sends a request containing the given fields and waits for the target
- * message.
- *
- * @param fields
- * The fields of the message
- * @param data
- * The payload of the message
- * @return The reply message
- * @throws PluginException
- * if the request could not be sent
- */
- private Reply performRequest(SimpleFieldSet fields, Bucket data) throws PluginException {
- String identifier = "FCP-Command-" + System.currentTimeMillis() + "-" + counter.getAndIncrement();
- Reply reply = new Reply();
- PluginIdentifier pluginIdentifier = new PluginIdentifier(WOT_PLUGIN_NAME, identifier);
- replies.put(pluginIdentifier, reply);
-
- logger.log(Level.FINE, String.format("Sending FCP Request: %s", fields.get("Message")));
- synchronized (reply) {
- try {
- pluginConnector.sendRequest(WOT_PLUGIN_NAME, identifier, fields, data);
- while (reply.getFields() == null) {
- try {
- reply.wait();
- } catch (InterruptedException ie1) {
- logger.log(Level.WARNING, String.format("Got interrupted while waiting for reply on %s.", fields.get("Message")), ie1);
- }
- }
- } finally {
- replies.remove(pluginIdentifier);
- }
- }
- logger.log(Level.FINEST, String.format("Received FCP Response for %s: %s", fields.get("Message"), (reply.getFields() != null) ? reply.getFields().get("Message") : null));
- if ((reply.getFields() == null) || "Error".equals(reply.getFields().get("Message"))) {
- throw new PluginException("Could not perform request for " + fields.get("Message"));
- }
- return reply;
- }
-
- /**
- * Notifies the connector that a plugin reply was received.
- *
- * @param receivedReplyEvent
- * The event
- */
- @Subscribe
- public void receivedReply(ReceivedReplyEvent receivedReplyEvent) {
- PluginIdentifier pluginIdentifier = new PluginIdentifier(receivedReplyEvent.pluginName(), receivedReplyEvent.identifier());
- Reply reply = replies.remove(pluginIdentifier);
- if (reply == null) {
- return;
- }
- logger.log(Level.FINEST, String.format("Received Reply from Plugin: %s", receivedReplyEvent.fieldSet().get("Message")));
- synchronized (reply) {
- reply.setFields(receivedReplyEvent.fieldSet());
- reply.setData(receivedReplyEvent.data());
- reply.notify();
- }
- }
-
- /**
- * Container for the data of the reply from a plugin.
- */
- private static class Reply {
-
- /** The fields of the reply. */
- private SimpleFieldSet fields;
-
- /** The payload of the reply. */
- private Bucket data;
-
- /** Empty constructor. */
- public Reply() {
- /* do nothing. */
- }
-
- /**
- * Returns the fields of the reply.
- *
- * @return The fields of the reply
- */
- public SimpleFieldSet getFields() {
- return fields;
- }
-
- /**
- * Sets the fields of the reply.
- *
- * @param fields
- * The fields of the reply
- */
- public void setFields(SimpleFieldSet fields) {
- this.fields = fields;
- }
-
- /**
- * Returns the payload of the reply.
- *
- * @return The payload of the reply (may be {@code null})
- */
- @SuppressWarnings("unused")
- public Bucket getData() {
- return data;
- }
-
- /**
- * Sets the payload of the reply.
- *
- * @param data
- * The payload of the reply (may be {@code null})
- */
- public void setData(Bucket data) {
- this.data = data;
- }
-
- }
-
- /**
- * Helper method to create {@link SimpleFieldSet}s with terser code.
- */
- private static class SimpleFieldSetConstructor {
-
- /** The field set being created. */
- private SimpleFieldSet simpleFieldSet;
-
- /**
- * Creates a new simple field set constructor.
- *
- * @param shortLived
- * {@code true} if the resulting simple field set should be
- * short-lived, {@code false} otherwise
- */
- private SimpleFieldSetConstructor(boolean shortLived) {
- simpleFieldSet = new SimpleFieldSet(shortLived);
- }
-
- //
- // ACCESSORS
- //
-
- /**
- * Returns the created simple field set.
- *
- * @return The created simple field set
- */
- public SimpleFieldSet get() {
- return simpleFieldSet;
- }
-
- /**
- * Sets the field with the given name to the given value.
- *
- * @param name
- * The name of the fleld
- * @param value
- * The value of the field
- * @return This constructor (for method chaining)
- */
- public SimpleFieldSetConstructor put(String name, String value) {
- simpleFieldSet.putOverwrite(name, value);
- return this;
- }
-
- //
- // ACTIONS
- //
-
- /**
- * Creates a new simple field set constructor.
- *
- * @return The created simple field set constructor
- */
- public static SimpleFieldSetConstructor create() {
- return create(true);
- }
-
- /**
- * Creates a new simple field set constructor.
- *
- * @param shortLived
- * {@code true} if the resulting simple field set should be
- * short-lived, {@code false} otherwise
- * @return The created simple field set constructor
- */
- public static SimpleFieldSetConstructor create(boolean shortLived) {
- return new SimpleFieldSetConstructor(shortLived);
- }
-
- }
-
- /**
- * Container for identifying plugins. Plugins are identified by their plugin
- * name and their unique identifier.
- */
- private static class PluginIdentifier {
-
- /** The plugin name. */
- private final String pluginName;
-
- /** The plugin identifier. */
- private final String identifier;
-
- /**
- * Creates a new plugin identifier.
- *
- * @param pluginName
- * The name of the plugin
- * @param identifier
- * The identifier of the plugin
- */
- public PluginIdentifier(String pluginName, String identifier) {
- this.pluginName = pluginName;
- this.identifier = identifier;
- }
-
- //
- // OBJECT METHODS
- //
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int hashCode() {
- return pluginName.hashCode() ^ identifier.hashCode();
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean equals(Object object) {
- if (!(object instanceof PluginIdentifier)) {
- return false;
- }
- PluginIdentifier pluginIdentifier = (PluginIdentifier) object;
- return pluginName.equals(pluginIdentifier.pluginName) && identifier.equals(pluginIdentifier.identifier);
- }
-
- }
-
-}
+++ /dev/null
-/*
- * Sone - WebOfTrustException.java - Copyright © 2010–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-/**
- * Exception that signals an error processing web of trust identities, mostly
- * when communicating with the web of trust plugin.
- */
-public class WebOfTrustException extends Exception {
-
- /**
- * Creates a new web of trust exception.
- */
- public WebOfTrustException() {
- super();
- }
-
- /**
- * Creates a new web of trust exception.
- *
- * @param message
- * The message of the exception
- */
- public WebOfTrustException(String message) {
- super(message);
- }
-
- /**
- * Creates a new web of trust exception.
- *
- * @param cause
- * The cause of the exception
- */
- public WebOfTrustException(Throwable cause) {
- super(cause);
- }
-
- /**
- * Creates a new web of trust exception.
- *
- * @param message
- * The message of the exception
- * @param cause
- * The cause of the exception
- */
- public WebOfTrustException(String message, Throwable cause) {
- super(message, cause);
- }
-
-}
+++ /dev/null
-/*
- * Sone - IdentityAddedEvent.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot.event;
-
-import net.pterodactylus.sone.freenet.wot.Identity;
-import net.pterodactylus.sone.freenet.wot.OwnIdentity;
-
-/**
- * Event that signals that an {@link Identity} was added.
- */
-public class IdentityAddedEvent extends IdentityEvent {
-
- /**
- * Creates a new “identity added” event.
- *
- * @param ownIdentity
- * The own identity that added the identity
- * @param identity
- * The identity that was added
- */
- public IdentityAddedEvent(OwnIdentity ownIdentity, Identity identity) {
- super(ownIdentity, identity);
- }
-
-}
+++ /dev/null
-/*
- * Sone - IdentityEvent.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot.event;
-
-import net.pterodactylus.sone.freenet.wot.Identity;
-import net.pterodactylus.sone.freenet.wot.OwnIdentity;
-
-/**
- * Base class for {@link Identity} events.
- */
-public abstract class IdentityEvent {
-
- /** The own identity this event relates to. */
- private final OwnIdentity ownIdentity;
-
- /** The identity this event is about. */
- private final Identity identity;
-
- /**
- * Creates a new identity-based event.
- *
- * @param ownIdentity
- * The own identity that relates to the identity
- * @param identity
- * The identity this event is about
- */
- protected IdentityEvent(OwnIdentity ownIdentity, Identity identity) {
- this.ownIdentity = ownIdentity;
- this.identity = identity;
- }
-
- //
- // ACCESSORS
- //
-
- /**
- * Returns the own identity this event relates to.
- *
- * @return The own identity this event relates to
- */
- public OwnIdentity ownIdentity() {
- return ownIdentity;
- }
-
- /**
- * Returns the identity this event is about.
- *
- * @return The identity this event is about
- */
- public Identity identity() {
- return identity;
- }
-
- @Override
- public int hashCode() {
- return ownIdentity().hashCode() ^ identity().hashCode();
- }
-
- @Override
- public boolean equals(Object object) {
- if ((object == null) || !object.getClass().equals(getClass())) {
- return false;
- }
- IdentityEvent identityEvent = (IdentityEvent) object;
- return ownIdentity().equals(identityEvent.ownIdentity()) && identity().equals(identityEvent.identity());
- }
-
-}
+++ /dev/null
-/*
- * Sone - IdentityRemovedEvent.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot.event;
-
-import net.pterodactylus.sone.freenet.wot.Identity;
-import net.pterodactylus.sone.freenet.wot.OwnIdentity;
-
-/**
- * Event that signals that an {@link Identity} was removed.
- */
-public class IdentityRemovedEvent extends IdentityEvent {
-
- /**
- * Creates a new “identity removed” event.
- *
- * @param ownIdentity
- * The own identity that removed the identity
- * @param identity
- * The identity that was removed
- */
- public IdentityRemovedEvent(OwnIdentity ownIdentity, Identity identity) {
- super(ownIdentity, identity);
- }
-
-}
+++ /dev/null
-/*
- * Sone - IdentityUpdatedEvent.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot.event;
-
-import net.pterodactylus.sone.freenet.wot.Identity;
-import net.pterodactylus.sone.freenet.wot.OwnIdentity;
-
-/**
- * Event that signals that an {@link Identity} was updated.
- */
-public class IdentityUpdatedEvent extends IdentityEvent {
-
- /**
- * Creates a new “identity updated” event.
- *
- * @param ownIdentity
- * The own identity that tracks the identity
- * @param identity
- * The identity that was updated
- */
- public IdentityUpdatedEvent(OwnIdentity ownIdentity, Identity identity) {
- super(ownIdentity, identity);
- }
-
-}
+++ /dev/null
-/*
- * Sone - OwnIdentityAddedEvent.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot.event;
-
-import net.pterodactylus.sone.freenet.wot.OwnIdentity;
-
-/**
- * Event that signals that an {@link OwnIdentity} was added.
- */
-public class OwnIdentityAddedEvent extends OwnIdentityEvent {
-
- /**
- * Creates new “own identity added” event.
- *
- * @param ownIdentity
- * The own identity that was added
- */
- public OwnIdentityAddedEvent(OwnIdentity ownIdentity) {
- super(ownIdentity);
- }
-
-}
+++ /dev/null
-/*
- * Sone - OwnIdentityEvent.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot.event;
-
-import net.pterodactylus.sone.freenet.wot.OwnIdentity;
-
-/**
- * Base class for {@link OwnIdentity} events.
- */
-public abstract class OwnIdentityEvent {
-
- /** The own identity this event is about. */
- private final OwnIdentity ownIdentity;
-
- /**
- * Creates a new own identity-based event.
- *
- * @param ownIdentity
- * The own identity this event is about
- */
- protected OwnIdentityEvent(OwnIdentity ownIdentity) {
- this.ownIdentity = ownIdentity;
- }
-
- //
- // ACCESSORS
- //
-
- /**
- * Returns the own identity this event is about.
- *
- * @return The own identity this event is about
- */
- public OwnIdentity ownIdentity() {
- return ownIdentity;
- }
-
- @Override
- public int hashCode() {
- return ownIdentity().hashCode();
- }
-
- @Override
- public boolean equals(Object object) {
- if ((object == null) || !object.getClass().equals(getClass())) {
- return false;
- }
- OwnIdentityEvent ownIdentityEvent = (OwnIdentityEvent) object;
- return ownIdentity().equals(ownIdentityEvent.ownIdentity());
- }
-
-}
+++ /dev/null
-/*
- * Sone - OwnIdentityRemovedEvent.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot.event;
-
-import net.pterodactylus.sone.freenet.wot.OwnIdentity;
-
-/**
- * Event that signals that an {@link OwnIdentity} was removed.
- */
-public class OwnIdentityRemovedEvent extends OwnIdentityEvent {
-
- /**
- * Creates a new “own identity removed” event.
- *
- * @param ownIdentity
- * The own identity that was removed
- */
- public OwnIdentityRemovedEvent(OwnIdentity ownIdentity) {
- super(ownIdentity);
- }
-
-}
+++ /dev/null
-/*
- * Sone - ListNotification.java - Copyright © 2010–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.notify;
-
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.List;
-import java.util.concurrent.CopyOnWriteArrayList;
-
-import net.pterodactylus.util.notify.TemplateNotification;
-import net.pterodactylus.util.template.Template;
-
-/**
- * Notification that maintains a list of new elements.
- *
- * @param <T>
- * The type of the items
- */
-public class ListNotification<T> extends TemplateNotification {
-
- /** The key under which to store the elements in the template. */
- private final String key;
-
- /** The list of new elements. */
- private final List<T> elements = new CopyOnWriteArrayList<>();
-
- /**
- * Creates a new list notification.
- *
- * @param id
- * The ID of the notification
- * @param key
- * The key under which to store the elements in the template
- * @param template
- * The template to render
- */
- public ListNotification(String id, String key, Template template) {
- this(id, key, template, true);
- }
-
- /**
- * Creates a new list notification.
- *
- * @param id
- * The ID of the notification
- * @param key
- * The key under which to store the elements in the template
- * @param template
- * The template to render
- * @param dismissable
- * {@code true} if this notification should be dismissable by the
- * user, {@code false} otherwise
- */
- public ListNotification(String id, String key, Template template, boolean dismissable) {
- super(id, System.currentTimeMillis(), System.currentTimeMillis(), dismissable, template);
- this.key = key;
- template.getInitialContext().set(key, elements);
- }
-
- /**
- * Creates a new list notification that copies its ID and the template from
- * the given list notification.
- *
- * @param listNotification
- * The list notification to copy
- */
- public ListNotification(ListNotification<T> listNotification) {
- super(listNotification.getId(), listNotification.getCreatedTime(), listNotification.getLastUpdatedTime(), listNotification.isDismissable(), new Template());
- this.key = listNotification.key;
- getTemplate().add(listNotification.getTemplate());
- getTemplate().getInitialContext().set(key, elements);
- }
-
- //
- // ACTIONS
- //
-
- /**
- * Returns the current list of elements.
- *
- * @return The current list of elements
- */
- public List<T> getElements() {
- return new ArrayList<>(elements);
- }
-
- /**
- * Sets the elements to show in this notification. This method will not call
- * {@link #touch()}.
- *
- * @param elements
- * The elements to show
- */
- public void setElements(Collection<? extends T> elements) {
- this.elements.clear();
- this.elements.addAll(elements);
- touch();
- }
-
- /**
- * Returns whether there are any new elements.
- *
- * @return {@code true} if there are no new elements, {@code false} if there
- * are new elements
- */
- public boolean isEmpty() {
- return elements.isEmpty();
- }
-
- /**
- * Adds a discovered element.
- *
- * @param element
- * The new element
- */
- public void add(T element) {
- elements.add(element);
- touch();
- }
-
- /**
- * Removes the given element from the list of new elements.
- *
- * @param element
- * The element to remove
- */
- public void remove(T element) {
- while (elements.remove(element)) {
- /* do nothing, just remove all instances of the element. */
- }
- if (elements.isEmpty()) {
- dismiss();
- }
- touch();
- }
-
- //
- // ABSTRACTNOTIFICATION METHODS
- //
-
- /**
- * {@inheritDoc}
- */
- @Override
- public void dismiss() {
- super.dismiss();
- elements.clear();
- }
-
- //
- // OBJECT METHODS
- //
-
- /**
- * {@inheritDoc}
- */
- @Override
- public int hashCode() {
- int hashCode = super.hashCode();
- for (T element : elements) {
- hashCode ^= element.hashCode();
- }
- return hashCode;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public boolean equals(Object object) {
- if (!(object instanceof ListNotification)) {
- return false;
- }
- ListNotification<?> listNotification = (ListNotification<?>) object;
- if (!super.equals(listNotification)) {
- return false;
- }
- if (!key.equals(listNotification.key)) {
- return false;
- }
- return elements.equals(listNotification.elements);
- }
-
-}
import java.util.Map;
+import net.pterodactylus.sone.freenet.Translation;
import net.pterodactylus.util.template.Filter;
import net.pterodactylus.util.template.TemplateContext;
-import freenet.l10n.BaseL10n;
/**
* {@link Filter} implementation that replaces a {@link Long} with a value of
- * {@code 0} by a {@link String} from an {@link BaseL10n l10n handler}.
+ * {@code 0} by a {@link String} from a {@link Translation translation}.
*/
public class UnknownDateFilter implements Filter {
- /** The l10n handler. */
- private BaseL10n l10nHandler;
+ /** The translation. */
+ private final Translation translation;
/** The key for the text to show. */
private final String unknownKey;
/**
* Creates a new unknown date filter.
*
- * @param l10nHandler
- * The l10n handler
- * @param unknownKey
- * The key of the text to show
+ * @param translation The translation
+ * @param unknownKey The key of the text to show
*/
- public UnknownDateFilter(BaseL10n l10nHandler, String unknownKey) {
- this.l10nHandler = l10nHandler;
+ public UnknownDateFilter(Translation translation, String unknownKey) {
+ this.translation = translation;
this.unknownKey = unknownKey;
}
public Object format(TemplateContext templateContext, Object data, Map<String, Object> parameters) {
if (data instanceof Long) {
if ((Long) data == 0) {
- return l10nHandler.getString(unknownKey);
+ return translation.translate(unknownKey);
}
}
return data;
import static com.google.common.collect.FluentIterable.from;
import static java.util.logging.Logger.getLogger;
-import static net.pterodactylus.util.template.TemplateParser.parse;
-import java.io.StringReader;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import net.pterodactylus.sone.data.PostReply;
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.freenet.L10nFilter;
+import net.pterodactylus.sone.freenet.Translation;
import net.pterodactylus.sone.main.Loaders;
import net.pterodactylus.sone.main.PluginHomepage;
import net.pterodactylus.sone.main.PluginVersion;
import freenet.clients.http.SessionManager;
import freenet.clients.http.SessionManager.Session;
import freenet.clients.http.ToadletContext;
-import freenet.l10n.BaseL10n;
import com.codahale.metrics.*;
import com.google.common.base.Optional;
private final PageToadletRegistry pageToadletRegistry;
private final MetricRegistry metricRegistry;
+ private final Translation translation;
/** The “new Sone” notification. */
private final ListNotification<Sone> newSoneNotification;
ParserFilter parserFilter, ShortenFilter shortenFilter,
RenderFilter renderFilter,
LinkedElementRenderFilter linkedElementRenderFilter,
- PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry) {
+ PageToadletRegistry pageToadletRegistry, MetricRegistry metricRegistry, Translation translation, L10nFilter l10nFilter) {
this.sonePlugin = sonePlugin;
this.loaders = loaders;
this.listNotificationFilter = listNotificationFilter;
this.linkedElementRenderFilter = linkedElementRenderFilter;
this.pageToadletRegistry = pageToadletRegistry;
this.metricRegistry = metricRegistry;
+ this.l10nFilter = l10nFilter;
+ this.translation = translation;
formPassword = sonePlugin.pluginRespirator().getToadletContainer().getFormPassword();
soneTextParser = new SoneTextParser(getCore(), getCore());
- l10nFilter = new L10nFilter(getL10n());
this.templateContextFactory = templateContextFactory;
templateContextFactory.addTemplateObject("webInterface", this);
return listNotificationFilter.filterNotifications(notificationManager.getNotifications(), currentSone);
}
- /**
- * Returns the l10n helper of the node.
- *
- * @return The node’s l10n helper
- */
- public BaseL10n getL10n() {
- return sonePlugin.l10n().getBase();
+ public Translation getTranslation() {
+ return translation;
}
/**
import freenet.clients.http.Toadlet;
import freenet.clients.http.ToadletContext;
import freenet.clients.http.ToadletContextClosedException;
-import freenet.l10n.NodeL10n;
import freenet.support.MultiValueTable;
import freenet.support.api.HTTPRequest;
* if the toadlet context is closed
*/
public void handleMethodGET(URI uri, HTTPRequest httpRequest, ToadletContext toadletContext) throws IOException, ToadletContextClosedException {
- handleRequest(new FreenetRequest(uri, Method.GET, httpRequest, toadletContext, NodeL10n.getBase(), sessionManager));
+ handleRequest(new FreenetRequest(uri, Method.GET, httpRequest, toadletContext, sessionManager));
}
/**
* if the toadlet context is closed
*/
public void handleMethodPOST(URI uri, HTTPRequest httpRequest, ToadletContext toadletContext) throws IOException, ToadletContextClosedException {
- handleRequest(new FreenetRequest(uri, Method.POST, httpRequest, toadletContext, NodeL10n.getBase(), sessionManager));
+ handleRequest(new FreenetRequest(uri, Method.POST, httpRequest, toadletContext, sessionManager));
}
/**
--- /dev/null
+package net.pterodactylus.sone.freenet
+
+import freenet.l10n.*
+import java.util.*
+
+/**
+ * [Translation] implementation based on Fred’s [BaseL10n].
+ */
+class BaseL10nTranslation(private val baseL10n: BaseL10n) : Translation {
+
+ override val currentLocale: Locale
+ get() = Locale(baseL10n.selectedLanguage.shortCode)
+
+ override fun translate(key: String): String = baseL10n.getString(key)
+
+}
--- /dev/null
+package net.pterodactylus.sone.freenet
+
+import freenet.keys.*
+import freenet.support.Base64.*
+
+val FreenetURI.routingKeyString: String get() = encode(routingKey)
--- /dev/null
+/*
+ * Sone - L10nFilter.java - Copyright © 2010–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet
+
+import net.pterodactylus.util.template.*
+import java.text.*
+
+/**
+ * [Filter] implementation replaces [String] values with their
+ * translated equivalents.
+ */
+class L10nFilter(private val translation: Translation) : Filter {
+
+ override fun format(templateContext: TemplateContext?, data: Any?, parameters: Map<String, Any>?): String {
+ val parameterValues = getParameters(data, parameters)
+ val text = getText(data)
+ return if (parameterValues.isEmpty()) {
+ translation.translate(text)
+ } else
+ MessageFormat(translation.translate(text), translation.currentLocale).format(parameterValues.toTypedArray())
+ }
+
+ private fun getText(data: Any?) = (data as? L10nText)?.text ?: data.toString()
+
+ private fun getParameters(data: Any?, parameters: Map<String, Any>?) =
+ if (data is L10nText)
+ data.parameters
+ else
+ (parameters ?: emptyMap()).let { params ->
+ generateSequence(0) { it + 1 }
+ .takeWhile { it.toString() in params }
+ .map { params[it.toString()] }
+ .toList()
+ }
+
+}
--- /dev/null
+/**
+ * Sone - Translation.kt - Copyright © 2019 David ‘Bombe’ Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet
+
+import java.util.*
+
+/**
+ * Facade for Fred’s [freenet.l10n.BaseL10n] object.
+ */
+interface Translation {
+
+ /** The currently selected locale. */
+ val currentLocale: Locale
+
+ /**
+ * Returns the translated string for the given key, defaulting to `""`.
+ *
+ * @param key The key to return the translated string for
+ * @return The translated string, or `""` if there is no translation
+ */
+ fun translate(key: String): String
+
+}
--- /dev/null
+/* Fred’s plugin stuff is mostly deprecated. ¯\_(ツ)_/¯ */
+@file:Suppress("DEPRECATION")
+
+package net.pterodactylus.sone.freenet.plugin
+
+import freenet.pluginmanager.*
+import freenet.support.*
+import freenet.support.api.*
+import kotlinx.coroutines.*
+import kotlinx.coroutines.channels.*
+import javax.inject.*
+
+/**
+ * [PluginConnector] implementation that uses a [PluginRespiratorFacade] and coroutines to send
+ * a request to another plugin and receive a reply.
+ */
+class FredPluginConnector @Inject constructor(private val pluginRespiratorFacade: PluginRespiratorFacade) : PluginConnector {
+
+ override suspend fun sendRequest(pluginName: String, fields: SimpleFieldSet, data: Bucket?): PluginReply {
+ val receivedReply = Channel<PluginReply>()
+ val responseReceiver = FredPluginTalker { _, _, responseFields, responseData ->
+ GlobalScope.launch {
+ receivedReply.send(PluginReply(responseFields, responseData))
+ }
+ }
+ try {
+ val pluginTalker = pluginRespiratorFacade.getPluginTalker(responseReceiver, pluginName, "")
+ pluginTalker.send(fields, data)
+ return receivedReply.receive()
+ } catch (e: PluginNotFoundException) {
+ throw PluginException(cause = e)
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Sone - PluginConnector.kt - Copyright © 2010–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.plugin
+
+import freenet.support.*
+import freenet.support.api.*
+
+/**
+ * Interface for talking to other plugins. Other plugins are identified by their
+ * name and a unique connection identifier.
+ */
+interface PluginConnector {
+
+ /**
+ * Sends a message to another plugin running in the same node.
+ *
+ * @param pluginName The fully qualified name of the plugin
+ * @param fields The message being sent
+ * @param data Optional data
+ * @return The reply from the plugin
+ * @throws PluginException if the plugin identified by [pluginName] does not exist
+ */
+ @Throws(PluginException::class)
+ suspend fun sendRequest(pluginName: String, fields: SimpleFieldSet, data: Bucket? = null): PluginReply
+
+}
+
+data class PluginReply(val fields: SimpleFieldSet, val data: Bucket?)
--- /dev/null
+/*
+ * Sone - PluginException.java - Copyright © 2010–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.plugin
+
+import net.pterodactylus.sone.freenet.wot.*
+
+/**
+ * Exception that signals an error when communicating with a plugin.
+ */
+class PluginException(message: String? = null, cause: Throwable? = null) : WebOfTrustException(message, cause)
--- /dev/null
+/**
+ * Sone - PluginRespiratorFacade.kt - Copyright © 2019 David ‘Bombe’ Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Yes, this handle Fred-based stuff that’s mostly deprecated. */
+@file:Suppress("DEPRECATION")
+
+package net.pterodactylus.sone.freenet.plugin
+
+import freenet.pluginmanager.*
+import freenet.support.*
+import freenet.support.api.*
+import javax.inject.*
+
+/**
+ * Facade for the only method of a [plugin respirator][PluginRespirator] that Sone actually uses,
+ * for easier testing.
+ */
+interface PluginRespiratorFacade {
+
+ @Throws(PluginNotFoundException::class)
+ fun getPluginTalker(pluginTalker: FredPluginTalker, pluginName: String, identifier: String): PluginTalkerFacade
+
+}
+
+/**
+ * Facade for a [plugin talker][PluginTalker], for easier testing.
+ */
+interface PluginTalkerFacade {
+
+ fun send(pluginParameters: SimpleFieldSet, data: Bucket?)
+
+}
+
+/**
+ * Fred-based [PluginRespiratorFacade] implementation that proxies the given real [PluginRespirator].
+ */
+class FredPluginRespiratorFacade @Inject constructor(private val pluginRespirator: PluginRespirator) : PluginRespiratorFacade {
+
+ override fun getPluginTalker(pluginTalker: FredPluginTalker, pluginName: String, identifier: String) =
+ FredPluginTalkerFacade(pluginRespirator.getPluginTalker(pluginTalker, pluginName, identifier))
+
+}
+
+/**
+ * Fred-based [PluginTalkerFacade] implementation that proxies the given real [PluginTalker].
+ */
+class FredPluginTalkerFacade(private val pluginTalker: PluginTalker) : PluginTalkerFacade {
+
+ override fun send(pluginParameters: SimpleFieldSet, data: Bucket?) =
+ pluginTalker.send(pluginParameters, data)
+
+}
--- /dev/null
+/*
+ * Sone - Context.java - Copyright © 2014–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+/**
+ * Custom container for the Web of Trust context. This allows easier
+ * configuration of dependency injection.
+ */
+class Context(val context: String)
--- /dev/null
+/*
+ * Sone - DefaultIdentity.java - Copyright © 2010–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+import java.util.Collections.*
+
+/**
+ * A Web of Trust identity.
+ */
+open class DefaultIdentity(private val id: String, private val nickname: String?, private val requestUri: String) : Identity {
+
+ private val contexts = mutableSetOf<String>().synchronized()
+ private val properties = mutableMapOf<String, String>().synchronized()
+ private val trustCache = mutableMapOf<OwnIdentity, Trust>().synchronized()
+
+ override fun getId() = id
+ override fun getNickname() = nickname
+ override fun getRequestUri() = requestUri
+ override fun getContexts() = synchronized(contexts) { contexts.toSet() }
+
+ override fun hasContext(context: String) = context in contexts
+
+ override fun setContexts(contexts: Set<String>) {
+ synchronized(this.contexts) {
+ this.contexts.clear()
+ this.contexts.addAll(contexts)
+ }
+ }
+
+ override fun addContext(context: String): Identity = apply {
+ synchronized(this.contexts) {
+ contexts += context
+ }
+ }
+
+ override fun removeContext(context: String): Identity = apply {
+ synchronized(this.contexts) {
+ contexts -= context
+ }
+ }
+
+ override fun getProperties() = synchronized(properties) { properties.toMap() }
+
+ override fun setProperties(properties: Map<String, String>) {
+ synchronized(this.properties) {
+ this.properties.clear()
+ this.properties.putAll(properties)
+ }
+ }
+
+ override fun getProperty(name: String) = synchronized(properties) { properties[name] }
+
+ override fun setProperty(name: String, value: String): Identity = apply {
+ synchronized(properties) {
+ properties[name] = value
+ }
+ }
+
+ override fun removeProperty(name: String): Identity = apply {
+ synchronized(properties) {
+ properties -= name
+ }
+ }
+
+ override fun getTrust(ownIdentity: OwnIdentity) = synchronized(trustCache) {
+ trustCache[ownIdentity]
+ }
+
+ override fun setTrust(ownIdentity: OwnIdentity, trust: Trust) = apply {
+ synchronized(trustCache) {
+ trustCache[ownIdentity] = trust
+ }
+ }
+
+ override fun removeTrust(ownIdentity: OwnIdentity) = apply {
+ synchronized(trustCache) {
+ trustCache -= ownIdentity
+ }
+ }
+
+ override fun hashCode() = id.hashCode()
+
+ override fun equals(other: Any?) = if (other !is Identity) {
+ false
+ } else {
+ other.id == getId()
+ }
+
+ override fun toString() = "${javaClass.simpleName}[id=$id,nickname=$nickname,contexts=$contexts,properties=$properties]"
+
+}
+
+private fun <T> Set<T>.synchronized(): MutableSet<T> = synchronizedSet(this)
+private fun <K, V> Map<K, V>.synchronized(): MutableMap<K, V> = synchronizedMap(this)
--- /dev/null
+/*
+ * Sone - DefaultOwnIdentity.java - Copyright © 2010–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+/**
+ * An own identity is an identity that the owner of the node has full control
+ * over.
+ */
+class DefaultOwnIdentity(id: String, nickname: String, requestUri: String, private val insertUri: String) : DefaultIdentity(id, nickname, requestUri), OwnIdentity {
+
+ override fun getInsertUri(): String {
+ return insertUri
+ }
+
+ override fun addContext(context: String) = super.addContext(context) as OwnIdentity
+
+ override fun removeContext(context: String) = super.removeContext(context) as OwnIdentity
+
+ override fun setProperty(name: String, value: String) = super.setProperty(name, value) as OwnIdentity
+
+ override fun removeProperty(name: String) = super.removeProperty(name) as OwnIdentity
+
+}
--- /dev/null
+/*
+ * Sone - IdentityChangeEventSender.java - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+import com.google.common.eventbus.*
+import net.pterodactylus.sone.freenet.wot.event.*
+
+/**
+ * Detects changes in [Identity]s trusted by multiple [OwnIdentity]s.
+ *
+ * @see IdentityChangeDetector
+ */
+class IdentityChangeEventSender(private val eventBus: EventBus, private val oldIdentities: Map<OwnIdentity, Collection<Identity>>) {
+
+ fun detectChanges(identities: Map<OwnIdentity, Collection<Identity>>) {
+ val identityChangeDetector = IdentityChangeDetector(oldIdentities.keys)
+ identityChangeDetector.onNewIdentity = addNewOwnIdentityAndItsTrustedIdentities(identities)
+ identityChangeDetector.onRemovedIdentity = removeOwnIdentityAndItsTrustedIdentities(oldIdentities)
+ identityChangeDetector.onUnchangedIdentity = detectChangesInTrustedIdentities(identities, oldIdentities)
+ identityChangeDetector.detectChanges(identities.keys)
+ }
+
+ private fun addNewOwnIdentityAndItsTrustedIdentities(newIdentities: Map<OwnIdentity, Collection<Identity>>) =
+ { identity: Identity ->
+ eventBus.post(OwnIdentityAddedEvent(identity as OwnIdentity))
+ newIdentities[identity]
+ ?.map { IdentityAddedEvent(identity, it) }
+ ?.forEach(eventBus::post) ?: Unit
+ }
+
+ private fun removeOwnIdentityAndItsTrustedIdentities(oldIdentities: Map<OwnIdentity, Collection<Identity>>) =
+ { identity: Identity ->
+ eventBus.post(OwnIdentityRemovedEvent(identity as OwnIdentity))
+ oldIdentities[identity]
+ ?.map { IdentityRemovedEvent(identity, it) }
+ ?.forEach(eventBus::post) ?: Unit
+ }
+
+ private fun detectChangesInTrustedIdentities(newIdentities: Map<OwnIdentity, Collection<Identity>>, oldIdentities: Map<OwnIdentity, Collection<Identity>>) =
+ { ownIdentity: Identity ->
+ val identityChangeDetector = IdentityChangeDetector(oldIdentities[ownIdentity as OwnIdentity]!!)
+ identityChangeDetector.onNewIdentity = { eventBus.post(IdentityAddedEvent(ownIdentity, it)) }
+ identityChangeDetector.onRemovedIdentity = { eventBus.post(IdentityRemovedEvent(ownIdentity, it)) }
+ identityChangeDetector.onChangedIdentity = { eventBus.post(IdentityUpdatedEvent(ownIdentity, it)) }
+ identityChangeDetector.detectChanges(newIdentities[ownIdentity]!!)
+ }
+
+}
--- /dev/null
+/*
+ * Sone - IdentityLoader.java - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+import com.google.common.base.*
+import com.google.inject.*
+import net.pterodactylus.sone.freenet.plugin.*
+import java.util.concurrent.TimeUnit.*
+import java.util.logging.*
+
+/**
+ * Loads [OwnIdentity]s and the [Identity]s they trust.
+ */
+class IdentityLoader @Inject constructor(private val webOfTrustConnector: WebOfTrustConnector, private val context: Context? = null) {
+
+ private val logger: Logger = Logger.getLogger(IdentityLoader::class.java.name)
+
+ @Throws(WebOfTrustException::class)
+ fun loadIdentities() =
+ time({ stopwatch, identities -> "Loaded ${identities.size} own identities in ${stopwatch.elapsed(MILLISECONDS) / 1000.0}s." }) {
+ webOfTrustConnector.loadAllOwnIdentities()
+ }.let(this::loadTrustedIdentitiesForOwnIdentities)
+
+ @Throws(PluginException::class)
+ private fun loadTrustedIdentitiesForOwnIdentities(ownIdentities: Collection<OwnIdentity>) =
+ ownIdentities
+ .also { logger.fine { "Getting trusted identities for ${it.size} own identities..." } }
+ .associateWith { ownIdentity ->
+ logger.fine { "Getting trusted identities for $ownIdentity..." }
+ if (ownIdentity.doesNotHaveCorrectContext()) {
+ logger.fine { "Skipping $ownIdentity because of incorrect context." }
+ emptySet()
+ } else {
+ logger.fine { "Loading trusted identities for $ownIdentity from WoT..." }
+ time({ stopwatch, identities -> "Loaded ${identities.size} identities for ${ownIdentity.nickname} in ${stopwatch.elapsed(MILLISECONDS) / 1000.0}s." }) {
+ webOfTrustConnector.loadTrustedIdentities(ownIdentity, context?.context)
+ }
+ }
+ }
+
+ private fun OwnIdentity.doesNotHaveCorrectContext() =
+ context?.let { it.context !in contexts } ?: false
+
+ private fun <R> time(logMessage: (Stopwatch, R) -> String, loader: () -> R) =
+ Stopwatch.createStarted().let { stopwatch ->
+ loader().also { logger.fine(logMessage(stopwatch, it)) }
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.freenet.wot
+
+import net.pterodactylus.util.service.Service
+
+import com.google.common.eventbus.EventBus
+import com.google.inject.ImplementedBy
+
+/**
+ * Connects to a [WebOfTrustConnector] and sends identity events to an
+ * [EventBus].
+ */
+@ImplementedBy(IdentityManagerImpl::class)
+interface IdentityManager : Service {
+
+ val isConnected: Boolean
+ val allOwnIdentities: Set<OwnIdentity>
+
+}
--- /dev/null
+/*
+ * Sone - IdentityManagerImpl.java - Copyright © 2010–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+import com.google.common.eventbus.*
+import com.google.inject.*
+import net.pterodactylus.util.service.*
+import java.util.concurrent.TimeUnit.*
+import java.util.logging.*
+import java.util.logging.Logger.*
+
+/**
+ * The identity manager takes care of loading and storing identities, their
+ * contexts, and properties. It does so in a way that does not expose errors via
+ * exceptions but it only logs them and tries to return sensible defaults.
+ *
+ *
+ * It is also responsible for polling identities from the Web of Trust plugin
+ * and sending events to the [EventBus] when [Identity]s and
+ * [OwnIdentity]s are discovered or disappearing.
+ */
+@Singleton
+class IdentityManagerImpl @Inject constructor(
+ private val eventBus: EventBus,
+ private val webOfTrustConnector: WebOfTrustConnector,
+ private val identityLoader: IdentityLoader
+) : AbstractService("Sone Identity Manager", false), IdentityManager {
+
+ private val currentOwnIdentities = mutableSetOf<OwnIdentity>()
+
+ override val isConnected: Boolean
+ get() = notThrowing { webOfTrustConnector.ping() }
+
+ override val allOwnIdentities: Set<OwnIdentity>
+ get() = synchronized(currentOwnIdentities) {
+ currentOwnIdentities.toSet()
+ }
+
+ override fun serviceRun() {
+ var oldIdentities = mapOf<OwnIdentity, Collection<Identity>>()
+
+ while (!shouldStop()) {
+ try {
+ val currentIdentities = identityLoader.loadIdentities()
+
+ val identityChangeEventSender = IdentityChangeEventSender(eventBus, oldIdentities)
+ identityChangeEventSender.detectChanges(currentIdentities)
+
+ oldIdentities = currentIdentities
+
+ synchronized(currentOwnIdentities) {
+ currentOwnIdentities.clear()
+ currentOwnIdentities.addAll(currentIdentities.keys)
+ }
+ } catch (wote1: WebOfTrustException) {
+ logger.log(Level.WARNING, "WoT has disappeared!", wote1)
+ } catch (e: Exception) {
+ logger.log(Level.SEVERE, "Uncaught exception in IdentityManager thread!", e)
+ }
+
+ /* wait a minute before checking again. */
+ sleep(SECONDS.toMillis(60))
+ }
+ }
+
+}
+
+private val logger: Logger = getLogger(IdentityManagerImpl::class.java.name)
+
+private fun notThrowing(action: () -> Unit): Boolean =
+ try {
+ action()
+ true
+ } catch (e: Exception) {
+ false
+ }
--- /dev/null
+/*
+ * Sone - WebOfTrustConnector.java - Copyright © 2010–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+import com.google.inject.*
+import freenet.support.*
+import kotlinx.coroutines.*
+import net.pterodactylus.sone.freenet.*
+import net.pterodactylus.sone.freenet.plugin.*
+import java.lang.String.*
+import java.util.logging.*
+import java.util.logging.Logger
+import java.util.logging.Logger.*
+
+/**
+ * Connector for the Web of Trust plugin.
+ */
+class PluginWebOfTrustConnector @Inject constructor(private val pluginConnector: PluginConnector) : WebOfTrustConnector {
+
+ private val logger: Logger = getLogger(PluginWebOfTrustConnector::class.java.name)
+
+ @Throws(PluginException::class)
+ override fun loadAllOwnIdentities(): Set<OwnIdentity> =
+ performRequest(SimpleFieldSetBuilder().put("Message", "GetOwnIdentities").get())
+ .fields
+ .parseIdentities { parseOwnIdentity(it) }
+
+ @Throws(PluginException::class)
+ override fun loadTrustedIdentities(ownIdentity: OwnIdentity, context: String?): Set<Identity> =
+ performRequest(SimpleFieldSetBuilder().put("Message", "GetIdentitiesByScore").put("Truster", ownIdentity.id).put("Selection", "+").put("Context", context ?: "").put("WantTrustValues", "true").get())
+ .fields
+ .parseIdentities { parseTrustedIdentity(it, ownIdentity) }
+
+ @Throws(PluginException::class)
+ override fun addContext(ownIdentity: OwnIdentity, context: String) {
+ performRequest(SimpleFieldSetBuilder().put("Message", "AddContext").put("Identity", ownIdentity.id).put("Context", context).get())
+ }
+
+ @Throws(PluginException::class)
+ override fun removeContext(ownIdentity: OwnIdentity, context: String) {
+ performRequest(SimpleFieldSetBuilder().put("Message", "RemoveContext").put("Identity", ownIdentity.id).put("Context", context).get())
+ }
+
+ override fun setProperty(ownIdentity: OwnIdentity, name: String, value: String) {
+ performRequest(SimpleFieldSetBuilder().put("Message", "SetProperty").put("Identity", ownIdentity.id).put("Property", name).put("Value", value).get())
+ }
+
+ override fun removeProperty(ownIdentity: OwnIdentity, name: String) {
+ performRequest(SimpleFieldSetBuilder().put("Message", "RemoveProperty").put("Identity", ownIdentity.id).put("Property", name).get())
+ }
+
+ override fun getTrust(ownIdentity: OwnIdentity, identity: Identity) =
+ performRequest(SimpleFieldSetBuilder().put("Message", "GetIdentity").put("Truster", ownIdentity.id).put("Identity", identity.id).get())
+ .fields
+ .parseTrust()
+
+ override fun setTrust(ownIdentity: OwnIdentity, identity: Identity, trust: Int, comment: String) {
+ performRequest(SimpleFieldSetBuilder().put("Message", "SetTrust").put("Truster", ownIdentity.id).put("Trustee", identity.id).put("Value", trust.toString()).put("Comment", comment).get())
+ }
+
+ override fun removeTrust(ownIdentity: OwnIdentity, identity: Identity) {
+ performRequest(SimpleFieldSetBuilder().put("Message", "RemoveTrust").put("Truster", ownIdentity.id).put("Trustee", identity.id).get())
+ }
+
+ override fun ping() {
+ performRequest(SimpleFieldSetBuilder().put("Message", "Ping").get())
+ }
+
+ private fun performRequest(fields: SimpleFieldSet): PluginReply {
+ logger.log(Level.FINE, format("Sending FCP Request: %s", fields.get("Message")))
+ return runBlocking {
+ pluginConnector.sendRequest(WOT_PLUGIN_NAME, fields).also {
+ logger.log(Level.FINEST, format("Received FCP Response for %s: %s", fields.get("Message"), it.fields.get("Message")))
+ if ("Error" == it.fields.get("Message")) {
+ throw PluginException("Could not perform request for " + fields.get("Message"))
+ }
+ }
+ }
+ }
+
+}
+
+private const val WOT_PLUGIN_NAME = "plugins.WebOfTrust.WebOfTrust"
+
+private fun <I> SimpleFieldSet.parseIdentities(parser: SimpleFieldSet.(Int) -> I) =
+ scanPrefix { "Identity$it" }
+ .map { parser(this, it) }
+ .toSet()
+
+private fun SimpleFieldSet.parseOwnIdentity(index: Int) =
+ DefaultOwnIdentity(get("Identity$index"), get("Nickname$index"), get("RequestURI$index"), get("InsertURI$index"))
+ .setContextsAndProperties(this@parseOwnIdentity, index)
+
+private fun SimpleFieldSet.parseTrustedIdentity(index: Int, ownIdentity: OwnIdentity) =
+ DefaultIdentity(get("Identity$index"), get("Nickname$index"), get("RequestURI$index"))
+ .setContextsAndProperties(this@parseTrustedIdentity, index)
+ .apply { setTrust(ownIdentity, this@parseTrustedIdentity.parseTrust(index.toString())) }
+
+private fun <I : Identity> I.setContextsAndProperties(simpleFieldSet: SimpleFieldSet, index: Int) = apply {
+ contexts = simpleFieldSet.contexts("Contexts$index.")
+ properties = simpleFieldSet.properties("Properties$index.")
+}
+
+private fun SimpleFieldSet.parseTrust(index: String = "") =
+ Trust(get("Trust$index")?.toIntOrNull(), get("Score$index")?.toIntOrNull(), get("Rank$index")?.toIntOrNull())
+
+private fun SimpleFieldSet.contexts(prefix: String) =
+ scanPrefix { "${prefix}Context$it" }
+ .map { get("${prefix}Context$it") }
+ .toSet()
+
+private fun SimpleFieldSet.properties(prefix: String) =
+ scanPrefix { "${prefix}Property${it}.Name" }
+ .map { get("${prefix}Property${it}.Name") to get("${prefix}Property${it}.Value") }
+ .toMap()
+
+private fun SimpleFieldSet.scanPrefix(prefix: (Int) -> String) =
+ generateSequence(0, Int::inc)
+ .takeWhile { get(prefix(it)) != null }
--- /dev/null
+/*
+ * Sone - Trust.java - Copyright © 2010–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+/**
+ * Container class for trust in the web of trust.
+ */
+data class Trust(val explicit: Int?, val implicit: Int?, val distance: Int?)
--- /dev/null
+package net.pterodactylus.sone.freenet.wot
+
+import net.pterodactylus.sone.freenet.plugin.*
+
+/**
+ * Connector for the web of trust plugin.
+ */
+interface WebOfTrustConnector {
+
+ /**
+ * Loads all own identities from the Web of Trust plugin.
+ *
+ * @return All own identity
+ * @throws WebOfTrustException if the own identities can not be loaded
+ */
+ @Throws(WebOfTrustException::class)
+ fun loadAllOwnIdentities(): Set<OwnIdentity>
+
+ /**
+ * Loads all identities that the given identities trusts with a score of
+ * more than 0 and the (optional) given context.
+ *
+ * @param ownIdentity The own identity
+ * @param context The context to filter, or `null`
+ * @return All trusted identities
+ * @throws PluginException if an error occured talking to the Web of Trust plugin
+ */
+ @Throws(PluginException::class)
+ fun loadTrustedIdentities(ownIdentity: OwnIdentity, context: String? = null): Set<Identity>
+
+ /**
+ * Adds the given context to the given identity.
+ *
+ * @param ownIdentity The identity to add the context to
+ * @param context The context to add
+ * @throws PluginException if an error occured talking to the Web of Trust plugin
+ */
+ @Throws(PluginException::class)
+ fun addContext(ownIdentity: OwnIdentity, context: String)
+
+ /**
+ * Removes the given context from the given identity.
+ *
+ * @param ownIdentity The identity to remove the context from
+ * @param context The context to remove
+ * @throws PluginException if an error occured talking to the Web of Trust plugin
+ */
+ @Throws(PluginException::class)
+ fun removeContext(ownIdentity: OwnIdentity, context: String)
+
+ /**
+ * Sets the property with the given name to the given value.
+ *
+ * @param ownIdentity The identity to set the property on
+ * @param name The name of the property to set
+ * @param value The value to set
+ * @throws PluginException if an error occured talking to the Web of Trust plugin
+ */
+ @Throws(PluginException::class)
+ fun setProperty(ownIdentity: OwnIdentity, name: String, value: String)
+
+ /**
+ * Removes the property with the given name.
+ *
+ * @param ownIdentity The identity to remove the property from
+ * @param name The name of the property to remove
+ * @throws PluginException if an error occured talking to the Web of Trust plugin
+ */
+ @Throws(PluginException::class)
+ fun removeProperty(ownIdentity: OwnIdentity, name: String)
+
+ /**
+ * Returns the trust for the given identity assigned to it by the given own
+ * identity.
+ *
+ * @param ownIdentity The own identity
+ * @param identity The identity to get the trust for
+ * @return The trust for the given identity
+ * @throws PluginException if an error occured talking to the Web of Trust plugin
+ */
+ @Throws(PluginException::class)
+ fun getTrust(ownIdentity: OwnIdentity, identity: Identity): Trust
+
+ /**
+ * Sets the trust for the given identity.
+ *
+ * @param ownIdentity The trusting identity
+ * @param identity The trusted identity
+ * @param trust The amount of trust (-100 thru 100)
+ * @param comment The comment or explanation of the trust value
+ * @throws PluginException if an error occured talking to the Web of Trust plugin
+ */
+ @Throws(PluginException::class)
+ fun setTrust(ownIdentity: OwnIdentity, identity: Identity, trust: Int, comment: String)
+
+ /**
+ * Removes any trust assignment of the given own identity for the given
+ * identity.
+ *
+ * @param ownIdentity The own identity
+ * @param identity The identity to remove all trust for
+ * @throws WebOfTrustException if an error occurs
+ */
+ @Throws(WebOfTrustException::class)
+ fun removeTrust(ownIdentity: OwnIdentity, identity: Identity)
+
+ /**
+ * Pings the Web of Trust plugin. If the plugin can not be reached, a
+ * [PluginException] is thrown.
+ *
+ * @throws PluginException if the plugin is not loaded
+ */
+ @Throws(PluginException::class)
+ fun ping()
+
+ /**
+ * Stops the web of trust connector.
+ */
+ fun stop() = Unit
+
+}
--- /dev/null
+/*
+ * Sone - WebOfTrustException.java - Copyright © 2010–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+/**
+ * Exception that signals an error processing web of trust identities, mostly
+ * when communicating with the web of trust plugin.
+ */
+open class WebOfTrustException(message: String? = null, cause: Throwable?) : Exception(message, cause)
--- /dev/null
+/*
+ * Sone - IdentityAddedEvent.java - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot.event
+
+import net.pterodactylus.sone.freenet.wot.*
+
+/**
+ * Event that signals that an [Identity] was added.
+ */
+data class IdentityAddedEvent(val ownIdentity: OwnIdentity, val identity: Identity)
--- /dev/null
+/*
+ * Sone - IdentityRemovedEvent.java - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot.event
+
+import net.pterodactylus.sone.freenet.wot.Identity
+import net.pterodactylus.sone.freenet.wot.OwnIdentity
+
+/**
+ * Event that signals that an [Identity] was removed.
+ */
+data class IdentityRemovedEvent(val ownIdentity: OwnIdentity, val identity: Identity)
--- /dev/null
+/*
+ * Sone - IdentityUpdatedEvent.java - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot.event
+
+import net.pterodactylus.sone.freenet.wot.*
+
+/**
+ * Event that signals that an [Identity] was updated.
+ */
+data class IdentityUpdatedEvent(val ownIdentity: OwnIdentity, val identity: Identity)
--- /dev/null
+/*
+ * Sone - OwnIdentityAddedEvent.java - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot.event
+
+import net.pterodactylus.sone.freenet.wot.OwnIdentity
+
+/**
+ * Event that signals that an [OwnIdentity] was added.
+ */
+data class OwnIdentityAddedEvent(val ownIdentity: OwnIdentity)
--- /dev/null
+/*
+ * Sone - OwnIdentityRemovedEvent.java - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot.event
+
+import net.pterodactylus.sone.freenet.wot.*
+
+/**
+ * Event that signals that an [OwnIdentity] was removed.
+ */
+data class OwnIdentityRemovedEvent(val ownIdentity: OwnIdentity)
import freenet.clients.http.*
import freenet.node.*
import freenet.pluginmanager.*
+import net.pterodactylus.sone.freenet.plugin.*
import javax.inject.Provider
import javax.inject.Singleton
class FreenetModule(private val pluginRespirator: PluginRespirator) : Module {
override fun configure(binder: Binder): Unit = binder.run {
- bind(PluginRespirator::class.java).toProvider(Provider<PluginRespirator> { pluginRespirator })
- pluginRespirator.node!!.let { node -> bind(Node::class.java).toProvider(Provider<Node> { node }) }
+ bind(PluginRespiratorFacade::class.java).toProvider(Provider { FredPluginRespiratorFacade(pluginRespirator) }).`in`(Singleton::class.java)
+ bind(PluginConnector::class.java).to(FredPluginConnector::class.java).`in`(Singleton::class.java)
+ bind(Node::class.java).toProvider(Provider { pluginRespirator.node })
bind(HighLevelSimpleClient::class.java).toProvider(Provider<HighLevelSimpleClient> { pluginRespirator.hlSimpleClient!! })
bind(ToadletContainer::class.java).toProvider(Provider<ToadletContainer> { pluginRespirator.toadletContainer })
bind(PageMaker::class.java).toProvider(Provider<PageMaker> { pluginRespirator.pageMaker })
import freenet.l10n.*
import net.pterodactylus.sone.database.*
import net.pterodactylus.sone.database.memory.*
+import net.pterodactylus.sone.freenet.*
import net.pterodactylus.sone.freenet.wot.*
import net.pterodactylus.util.config.*
import net.pterodactylus.util.config.ConfigurationException
bind(PluginYear::class.java).toInstance(PluginYear(sonePlugin.year))
bind(PluginHomepage::class.java).toInstance(PluginHomepage(sonePlugin.homepage))
bind(Database::class.java).to(MemoryDatabase::class.java).`in`(Singleton::class.java)
- bind(BaseL10n::class.java).toInstance(sonePlugin.l10n().base)
+ bind(Translation::class.java).toInstance(BaseL10nTranslation(sonePlugin.l10n().base))
loaders?.let { bind(Loaders::class.java).toInstance(it) }
bind(MetricRegistry::class.java).`in`(Singleton::class.java)
+ bind(WebOfTrustConnector::class.java).to(PluginWebOfTrustConnector::class.java).`in`(Singleton::class.java)
bindListener(Matchers.any(), object : TypeListener {
override fun <I> hear(typeLiteral: TypeLiteral<I>, typeEncounter: TypeEncounter<I>) {
--- /dev/null
+/*
+ * Sone - ListNotification.java - Copyright © 2010–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.notify
+
+import net.pterodactylus.util.notify.*
+import net.pterodactylus.util.template.*
+import java.lang.System.*
+import java.util.concurrent.*
+
+/**
+ * Notification that maintains a list of elements.
+ *
+ * @param <T>
+ * The type of the items
+ */
+class ListNotification<T> : TemplateNotification {
+
+ private val key: String
+ private val realElements = CopyOnWriteArrayList<T>()
+
+ val elements: List<T> get() = realElements.toList()
+
+ val isEmpty
+ get() = elements.isEmpty()
+
+ @JvmOverloads
+ constructor(id: String, key: String, template: Template, dismissable: Boolean = true) : super(id, currentTimeMillis(), currentTimeMillis(), dismissable, template) {
+ this.key = key
+ template.initialContext.set(key, realElements)
+ }
+
+ constructor(listNotification: ListNotification<T>) : super(listNotification.id, listNotification.createdTime, listNotification.lastUpdatedTime, listNotification.isDismissable, Template()) {
+ this.key = listNotification.key
+ template.add(listNotification.template)
+ template.initialContext.set(key, realElements)
+ }
+
+ fun setElements(elements: Collection<T>) {
+ realElements.clear()
+ realElements.addAll(elements.distinct())
+ touch()
+ }
+
+ fun add(element: T) {
+ if (element !in realElements) {
+ realElements.add(element)
+ touch()
+ }
+ }
+
+ fun remove(element: T) {
+ while (realElements.remove(element)) {
+ /* do nothing, just remove all instances of the element. */
+ }
+ if (realElements.isEmpty()) {
+ dismiss()
+ }
+ touch()
+ }
+
+ override fun dismiss() {
+ super.dismiss()
+ realElements.clear()
+ }
+
+ override fun hashCode() =
+ realElements.fold(super.hashCode()) { hash, element -> hash xor element.hashCode() }
+
+ override fun equals(other: Any?): Boolean {
+ if (other !is ListNotification<*>) {
+ return false
+ }
+ val listNotification = other as ListNotification<*>?
+ if (!super.equals(listNotification)) {
+ return false
+ }
+ return (key == listNotification.key) && (realElements == listNotification.realElements)
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.template
+
+import com.codahale.metrics.*
+import net.pterodactylus.sone.utils.*
+import net.pterodactylus.util.template.*
+
+/**
+ * [Filter] that renders a [Histogram] as a table row.
+ */
+class HistogramRenderer : Filter {
+
+ override fun format(templateContext: TemplateContext, data: Any?, parameters: Map<String, Any?>?): Any? {
+ templateContext["metricName"] = (parameters?.get("name") as String?)?.dotToCamel()?.let { "Page.Metrics.$it.Title" }
+ (data as? Histogram)?.snapshot?.run {
+ templateContext["count"] = data.count
+ templateContext["min"] = min
+ templateContext["max"] = max
+ templateContext["mean"] = mean
+ templateContext["median"] = median
+ templateContext["percentile75"] = get75thPercentile()
+ templateContext["percentile95"] = get95thPercentile()
+ templateContext["percentile98"] = get98thPercentile()
+ templateContext["percentile99"] = get99thPercentile()
+ templateContext["percentile999"] = get999thPercentile()
+ }
+ return template.render(templateContext)
+ }
+
+}
+
+private val template = """<tr>
+ <td><% metricName|l10n|html></td>
+ <td class="numeric"><% count|html></td>
+ <td class="numeric"><% min|duration scale=='μs'|html></td>
+ <td class="numeric"><% max|duration scale=='μs'|html></td>
+ <td class="numeric"><% mean|duration scale=='μs'|html></td>
+ <td class="numeric"><% median|duration scale=='μs'|html></td>
+ <td class="numeric"><% percentile75|duration scale=='μs'|html></td>
+ <td class="numeric"><% percentile95|duration scale=='μs'|html></td>
+ <td class="numeric"><% percentile98|duration scale=='μs'|html></td>
+ <td class="numeric"><% percentile99|duration scale=='μs'|html></td>
+ <td class="numeric"><% percentile999|duration scale=='μs'|html></td>
+</tr>""".asTemplate()
+
+private fun String.dotToCamel() =
+ split(".").joinToString("", transform = String::capitalize)
* Returns the value of [block] if `this` is false, returns `null` otherwise.
*/
fun <R> Boolean.ifFalse(block: () -> R): R? = if (!this) block() else null
+
+/**
+ * Returns `this` but runs the given block if `this` is `false`.
+ *
+ * @param block The block to run if `this` is `false`
+ * @return `this`
+ */
+fun Boolean.onFalse(block: () -> Unit): Boolean = this.also { if (!this) block() }
package net.pterodactylus.sone.web
import com.google.inject.*
-import freenet.l10n.*
import freenet.support.api.*
import net.pterodactylus.sone.core.*
import net.pterodactylus.sone.data.*
addFilter("unique", UniqueElementFilter())
addFilter("mod", ModFilter())
addFilter("paginate", PaginationFilter())
+ addFilter("render-histogram", HistogramRenderer())
addProvider(TemplateProvider.TEMPLATE_CONTEXT_PROVIDER)
addProvider(loaders.templateProvider)
ProfileAccessor(core)
@Provides
- fun getL10nFilter(l10n: BaseL10n) =
- L10nFilter(l10n)
+ fun getL10nFilter(translation: Translation) =
+ L10nFilter(translation)
@Provides
fun getParserFilter(core: Core, soneTextParser: SoneTextParser) =
LinkedElementsFilter(elementLoader)
@Provides
- fun getUnknownDateFilter(l10n: BaseL10n) =
- UnknownDateFilter(l10n, "View.Sone.Text.UnknownDate")
+ fun getUnknownDateFilter(translation: Translation) =
+ UnknownDateFilter(translation, "View.Sone.Text.UnknownDate")
@Provides
fun getImageLinkFilter(core: Core) =
override fun createJsonObject(request: FreenetRequest) =
createSuccessJsonObject()
- .put("value", webInterface.l10n.getString(request.parameters["key"]))
+ .put("value", webInterface.translation.translate(request.parameters["key"] ?: ""))
}
import freenet.clients.http.*
import freenet.clients.http.SessionManager.*
-import freenet.l10n.*
import freenet.support.api.*
+import net.pterodactylus.sone.freenet.*
import net.pterodactylus.util.web.*
import java.net.*
import java.util.UUID.*
open class FreenetRequest(uri: URI, method: Method,
val httpRequest: HTTPRequest,
val toadletContext: ToadletContext,
- val l10n: BaseL10n,
val sessionManager: SessionManager
) : Request(uri, method) {
package net.pterodactylus.sone.web.page
import freenet.clients.http.*
-import freenet.l10n.*
import freenet.support.api.*
import net.pterodactylus.sone.core.*
import net.pterodactylus.sone.web.*
import net.pterodactylus.util.web.*
import java.net.*
-class SoneRequest(uri: URI, method: Method, httpRequest: HTTPRequest, toadletContext: ToadletContext, l10n: BaseL10n, sessionManager: SessionManager,
- val core: Core,
- val webInterface: WebInterface
-) : FreenetRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager)
+class SoneRequest(uri: URI, method: Method, httpRequest: HTTPRequest, toadletContext: ToadletContext, sessionManager: SessionManager,
+ val core: Core,
+ val webInterface: WebInterface
+) : FreenetRequest(uri, method, httpRequest, toadletContext, sessionManager)
-fun FreenetRequest.toSoneRequest(core: Core, webInterface: WebInterface) = SoneRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager, core, webInterface)
+fun FreenetRequest.toSoneRequest(core: Core, webInterface: WebInterface) =
+ SoneRequest(uri, method, httpRequest, toadletContext, sessionManager, core, webInterface)
class MetricsPage @Inject constructor(webInterface: WebInterface, loaders: Loaders, templateRenderer: TemplateRenderer, private val metricsRegistry: MetricRegistry) : SoneTemplatePage(webInterface, loaders, templateRenderer, "Page.Metrics.Title") {
override fun handleRequest(soneRequest: SoneRequest, templateContext: TemplateContext) {
- addHistogram(templateContext, "sone.parsing.duration", "soneParsingDuration")
- addHistogram(templateContext, "sone.insert.duration", "soneInsertDuration")
- }
-
- private fun addHistogram(templateContext: TemplateContext, metricName: String, variablePrefix: String) {
- metricsRegistry.histogram(metricName).also { histogram ->
- templateContext["${variablePrefix}Count"] = histogram.count
- histogram.snapshot.also { snapshot ->
- templateContext["${variablePrefix}Min"] = snapshot.min
- templateContext["${variablePrefix}Max"] = snapshot.max
- templateContext["${variablePrefix}Median"] = snapshot.median
- templateContext["${variablePrefix}Mean"] = snapshot.mean
- templateContext["${variablePrefix}Percentile75"] = snapshot.get75thPercentile()
- templateContext["${variablePrefix}Percentile95"] = snapshot.get95thPercentile()
- templateContext["${variablePrefix}Percentile98"] = snapshot.get98thPercentile()
- templateContext["${variablePrefix}Percentile99"] = snapshot.get99thPercentile()
- templateContext["${variablePrefix}Percentile999"] = snapshot.get999thPercentile()
- }
- }
+ templateContext["histograms"] = metricsRegistry.histograms
}
}
val soneRescuer = soneRequest.core.getSoneRescuer(currentSone)
templateContext["soneRescuer"] = soneRescuer
if (soneRequest.isPOST) {
+ soneRequest.parameters["edition", 9]?.toIntOrNull()?.also {
+ if (it > -1) {
+ soneRescuer.setEdition(it.toLong())
+ }
+ }
if (soneRequest.parameters["fetch", 8] == "true") {
soneRescuer.startNextFetch()
}
- redirectTo("rescue.html")
+ throw RedirectException("rescue.html")
}
}
import freenet.clients.http.*
import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.freenet.*
import net.pterodactylus.sone.main.*
import net.pterodactylus.sone.utils.*
import net.pterodactylus.sone.web.*
templateRenderer: TemplateRenderer,
private val pageTitleKey: String? = null,
private val requiresLogin: Boolean = false,
- private val pageTitle: (FreenetRequest) -> String = { pageTitleKey?.let(webInterface.l10n::getString) ?: "" }
+ private val pageTitle: (FreenetRequest) -> String = { pageTitleKey?.let(webInterface.translation::translate) ?: "" }
) : FreenetTemplatePage(templateRenderer, loaders, "noPermission.html") {
private val core = webInterface.core
private val sessionProvider: SessionProvider = webInterface
+ protected val translation: Translation = webInterface.translation
protected fun getCurrentSone(toadletContext: ToadletContext, createSession: Boolean = true) =
sessionProvider.getCurrentSone(toadletContext, createSession)
private val String.urlEncode: String get() = URLEncoder.encode(this, "UTF-8")
override fun isEnabled(toadletContext: ToadletContext) =
- isEnabled(SoneRequest(toadletContext.uri, Method.GET, HTTPRequestImpl(toadletContext.uri, "GET"), toadletContext, webInterface.l10n, webInterface.sessionManager, core, webInterface))
+ isEnabled(SoneRequest(toadletContext.uri, Method.GET, HTTPRequestImpl(toadletContext.uri, "GET"), toadletContext, webInterface.sessionManager, core, webInterface))
open fun isEnabled(soneRequest: SoneRequest) = when {
requiresLogin && getCurrentSone(soneRequest.toadletContext) == null -> false
val bytes = uploadedFile.data.use { it.toByteArray() }
val bufferedImage = bytes.toImage()
if (bufferedImage == null) {
- templateContext["messages"] = soneRequest.l10n.getString("Page.UploadImage.Error.InvalidImage")
+ templateContext["messages"] = translation.translate("Page.UploadImage.Error.InvalidImage")
return
}
override fun getPageTitle(soneRequest: SoneRequest): String =
soneRequest.parameters["sone"]!!.let(soneRequest.core::getSone)?.let { sone ->
- "${SoneAccessor.getNiceName(sone)} - ${soneRequest.l10n.getString("Page.ViewSone.Title")}"
- } ?: soneRequest.l10n.getString("Page.ViewSone.Page.TitleWithoutSone")
+ "${SoneAccessor.getNiceName(sone)} - ${translation.translate("Page.ViewSone.Title")}"
+ } ?: translation.translate("Page.ViewSone.Page.TitleWithoutSone")
}
Page.Rescue.Text.Fetched=Die Sonerettung hat Version {0} Ihrer Sone herunter geladen. Bitte überprüfen Sie Ihre Nachrichten, Antworten und Ihr Profile. Bei Gefallen können Sie die Sone einfach entsperren.
Page.Rescue.Text.FetchedLast=Die Sonerettung hat die letzte verfügbare Version Ihrer Sone herunter geladen. Wenn bis jetzt keine Version dabei war, die Sie wiederherstellen möchten, haben Sie jetzt kein Glück.
Page.Rescue.Text.NotFetched=Die Sonerettung konnte Version {0} Ihrer Sone nicht herunter laden. Bitte versuchen Sie erneut, Version {0} herunter zu laden, oder versuchen Sie die nächstältere Version.
-Page.Rescue.Label.NextEdition=Nächste Version:
+Page.Rescue.Label.NextEdition=Nächste Version
Page.Rescue.Button.Fetch=Version herunter laden
Page.NoPermission.Title=Unberechtigter Zugriff - Sone
Page.Invalid.Page.Title=Ungültige Aktion ausgeführt
Page.Invalid.Text=Eine ungültige Aktion wurde ausgeführt, oder eine gültige Aktion hatte ungültige Parameter. Bitte kehren Sie zur {link}Hauptseite{/link} zurück und versuchen Sie Ihre Aktion erneut. Wenn der Fehler weiterhin besteht, haben Sie wahrscheinlich einen Programmierfehler gefunden.
+Page.Metrics.Title=Metriken
+Page.Metrics.Page.Title=Metriken
+Page.Metrics.SoneInsertDuration.Title=Hochladedauer einer Sone
+Page.Metrics.SoneParseDuration.Title=Parsdauer einer Sone
+Page.Metrics.ConfigurationSavingDuration.Title=Speicherdauer der Konfiguration
+
View.Search.Button.Search=Suchen
View.CreateSone.Text.WotIdentityRequired=Um eine Sone anzulegen, brauchen Sie eine Identität aus dem {link}„Web of Trust“ Plugin{/link}.
Page.Rescue.Text.Fetched=The Sone Rescuer has downloaded edition {0} of your Sone. Please check your posts, replies, and profile. If you like what the current Sone contains, just unlock it.
Page.Rescue.Text.FetchedLast=The Sone rescuer has downloaded the last available edition. If it did not manage to restore your Sone you are probably out of luck now.
Page.Rescue.Text.NotFetched=The Sone Rescuer could not download edition {0} of your Sone. Please either try again with edition {0}, or try the next older edition.
-Page.Rescue.Label.NextEdition=Next edition:
+Page.Rescue.Label.NextEdition=Next edition
Page.Rescue.Button.Fetch=Fetch edition
Page.NoPermission.Title=Unauthorized Access - Sone
Page.Metrics.Title=Metrics
Page.Metrics.Page.Title=Metrics
Page.Metrics.SoneInsertDuration.Title=Sone Insert Duration
-Page.Metrics.SoneParsingDuration.Title=Sone Parsing Duration
+Page.Metrics.SoneParseDuration.Title=Sone Parse Duration
+Page.Metrics.ConfigurationSaveDuration.Title=Configuration Save Duration
View.Search.Button.Search=Search
Page.Rescue.Text.Fetched=El modo rescate ha descargado la edición {0} de tu Sone. Por favor, comprueba tus publicaciones, respuestas y perfil. Si te gusta lo que tiene el Sone actual, desbloquealo.
Page.Rescue.Text.FetchedLast=El rescatador de Sone ha descargado la última versión disponibe. Si no ha conseguido restaurar tu Sone probablemente no te queda suerte.
Page.Rescue.Text.NotFetched=El rescatador de Sone no ha podido descargar la edición {0} de tu Sone. Por favor, vuelve ha intentarlo con la edición {0}, o prueba con la siguiente versión antigua.
-Page.Rescue.Label.NextEdition=Siguiente edición:
+Page.Rescue.Label.NextEdition=Siguiente edición
Page.Rescue.Button.Fetch=Obtener edición
Page.NoPermission.Title=Acceso desautorizado - Sone
Page.Invalid.Page.Title=Acción invalida realizada
Page.Invalid.Text=Se ha realizado una acción inválida, o la acción era válida pero los parámetros no. Por favor, vuelve al {link}índice{/link} e intentalo de nuevo. Si el error persiste, probablemente hayas encontrado un bug.
+Page.Metrics.Title=Metrics
+Page.Metrics.Page.Title=Metrics
+Page.Metrics.SoneInsertDuration.Title=Sone Insert Duration
+Page.Metrics.SoneParseDuration.Title=Sone Parse Duration
+Page.Metrics.ConfigurationSaveDuration.Title=Configuration Save Duration
+
View.Search.Button.Search=Buscar
View.CreateSone.Text.WotIdentityRequired=Para crear un Sone necesitas una identidad del {link}plugin Web of Trust{/link}.
Notification.SoneIsInserting.Text=Tu Sone sone://{0} está siendo insertado.
Notification.SoneIsInserted.Text=Tu Sone sone://{0} ha sido insertado en {1,number} {1,choice,0#segundos|1#segundo|1<segundos}.
Notification.SoneInsertAborted.Text=Tu Sone sone://{0} no pudo ser insertado.
-# 55-61
+# 55-61, 334–338
Page.Rescue.Text.Fetched=Le récupérateur de Sone a restauré la version {0} de votre Sone. Merci de vérifier vos messages, réponses et profile. Si les informations vous conviennent, débloquez la version.
Page.Rescue.Text.FetchedLast=Le récupérateur de Sone a restauré la dernière version disponible. Si vous ne souhaitiez pas récupérer une ancienne version de Sone. Considérez que vous n'avez pas de chance.
Page.Rescue.Text.NotFetched=Le récupérateur de Sone ne peut pas restaurer la version {0} de votre Sone. Merci de réessayer, ou essayez avec une version plus ancienne.
-Page.Rescue.Label.NextEdition=Prochaine version:
+Page.Rescue.Label.NextEdition=Prochaine version
Page.Rescue.Button.Fetch=Récupérer la version.
Page.NoPermission.Title=Accès non autorisé - Sone
Page.Invalid.Page.Title=Action invalide réalisée
Page.Invalid.Text=Une action invalide a été effectuée, ou l'action était valide mes les paramètres ne l'étaient pas. Veuillez retourner à la {link}page d'index{/link} et veuillez réessayer. Si l'erreur persiste, vous avez probablement trouvé un bug.
+Page.Metrics.Title=Metrics
+Page.Metrics.Page.Title=Metrics
+Page.Metrics.SoneInsertDuration.Title=Sone Insert Duration
+Page.Metrics.SoneParseDuration.Title=Sone Parse Duration
+Page.Metrics.ConfigurationSaveDuration.Title=Configuration Save Duration
+
View.Search.Button.Search=Recherche
View.CreateSone.Text.WotIdentityRequired=Pour créer un Sone vous avez besoin d'une identité venant du {link}plugin Web of Trust {/link}.
Notification.SoneIsInserting.Text=Votre Sone sone://{0} va maintenant être inséré.
Notification.SoneIsInserted.Text=votre Sone sone://{0} a été inséré dans {1,number} {1,choice,0#seconds|1#second|1<seconds}.
Notification.SoneInsertAborted.Text=Votre Sone sone://{0} ne peut pas être inséré.
-# 55-61
+# 55-61, 334–338
Page.Rescue.Text.Fetched=Sone復帰モードは{0}の版を読み込みました。投稿、返信、プロフィールなどを確認の上、問題がなければロックを解除してください。
Page.Rescue.Text.FetchedLast=Sone復帰モードは存在する全ての版を読み込みました。この時点で読み込めていない場合は恐らく復帰は不可能でしょう。
Page.Rescue.Text.NotFetched=Sone復帰モードは{0}の版を読み込むことはできませんでした。{0}の版を試してみるか、さらに次の版を試してみてください。
-Page.Rescue.Label.NextEdition=次の版:
+Page.Rescue.Label.NextEdition=次の版
Page.Rescue.Button.Fetch=版を取り出す
Page.NoPermission.Title=不正なアクセス - Sone
Page.Invalid.Page.Title=不正な操作が行われました
Page.Invalid.Text=不正な操作が行われたか、操作が正常でもパラメーターが不正である可能性があります。{link}インデックスページ{/link}に戻って試してみてください。同じ問題が何度も発生するようであればバグを見つけた可能性があります。
+Page.Metrics.Title=Metrics
+Page.Metrics.Page.Title=Metrics
+Page.Metrics.SoneInsertDuration.Title=Sone Insert Duration
+Page.Metrics.SoneParseDuration.Title=Sone Parse Duration
+Page.Metrics.ConfigurationSaveDuration.Title=Configuration Save Duration
+
View.Search.Button.Search=検索
View.CreateSone.Text.WotIdentityRequired=Soneを作成するのには{link}Web of Trustプラグイン{/link}のプロフィールが必要です。
Notification.SoneIsInserting.Text=あなたのSone sone://{0}は現在インサート中です。
Notification.SoneIsInserted.Text=あなたのSone sone://{0}は{1,number}{1,choice,0#秒|1#秒|1<秒}でインサートされました。
Notification.SoneInsertAborted.Text=あなたのSone sone://{0}のインサートに失敗しました。
-# 55-51, 67, 107, 465
+# 55-51, 67, 107, 334–338, 471
Page.Rescue.Text.Fetched=Sone-redderen har lastet ned utgave {0} av din Sone. Sjekk dine innlegg, svar og profil. Hvis du er fornøyd med redningen, kan du låse opp Sonen din.
Page.Rescue.Text.FetchedLast=Sone-redderen har lastet ned den siste tilgjengelige utgaven. Hvis den ikke klarte å redde Sonen din, er det lite annet å gjøre.
Page.Rescue.Text.NotFetched=Sone-redderen kunne ikke laste ned utgave {0} av din Sone. Enten prøv igjen på nytt med utgave {0}, eller prøv igjen med utgaven før.
-Page.Rescue.Label.NextEdition=Neste utgave:
+Page.Rescue.Label.NextEdition=Neste utgave
Page.Rescue.Button.Fetch=Hent utgave
Page.NoPermission.Title=Ikke-autorisert tilgang - Sone
Page.Invalid.Page.Title=Ugyldig handling
Page.Invalid.Text=En ugyldig handling ble gjort eller så var handlingens parametere ugyldig. Gå tilbake til {link}indeks{/link} og prøv på ny. Hvis feilmeldingen vedvarer har du sannsynligvis funnet en bug.
+Page.Metrics.Title=Metrics
+Page.Metrics.Page.Title=Metrics
+Page.Metrics.SoneInsertDuration.Title=Sone Insert Duration
+Page.Metrics.SoneParseDuration.Title=Sone Parse Duration
+Page.Metrics.ConfigurationSaveDuration.Title=Configuration Save Duration
+
View.Search.Button.Search=Søk
View.CreateSone.Text.WotIdentityRequired=For å lage en Sone trenger du et pseudonym fra {link}'Web of Trust'-tillegget{/link}.
Notification.SoneIsInserting.Text=Your Sone sone://{0} is now being inserted.
Notification.SoneIsInserted.Text=Your Sone sone://{0} has been inserted in {1,number} {1,choice,0#seconds|1#second|1<seconds}.
Notification.SoneInsertAborted.Text=Your Sone sone://{0} could not be inserted.
-# 55-61, 67, 107, 127-128, 315-317, 319-321, 465, 471-473
+# 55-61, 67, 107, 127-128, 315-317, 319-321, 334–338, 471, 477-479
Page.Rescue.Text.Fetched=Tryb Ratunkowy Sone pobrał edycję {0} twojego Sone. Sprawdź swoje posty, odpowiedzi oraz profil. Jeśli podoba ci się zawartość aktualnego Sone, to mozesz go odblokować.
Page.Rescue.Text.FetchedLast= Tryb Ratunkowy Sone pobrał ostatnią dostępną edycję. Jeśli nie udało się przywrócić twojego Sone, to nie masz teraz szczęścia.
Page.Rescue.Text.NotFetched=Tryb Ratunkowy Sone nie mógł sćiągnąć edycji {0} twojego Sone. Spróbuj pobrać ponownie edycję {0}, albo pobierz kolejną starszą edycję.
-Page.Rescue.Label.NextEdition=Następna edycja:
+Page.Rescue.Label.NextEdition=Następna edycja
Page.Rescue.Button.Fetch=Pobierz edycję
Page.NoPermission.Title=Nieupoważniony dostęp- Sone
Page.Invalid.Page.Title=Niepoprawne działanie
Page.Invalid.Text=Podjęto niepoprawne działanie, lub też działanie było poprawne, ale parametry nie. Wróć do{link}strony głównej{/link} i spróbuj ponownie. Jeśli problem będzie się utrzymywać to najprawdopodobniej znalazłeś błąd.
+Page.Metrics.Title=Metrics
+Page.Metrics.Page.Title=Metrics
+Page.Metrics.SoneInsertDuration.Title=Sone Insert Duration
+Page.Metrics.SoneParseDuration.Title=Sone Parse Duration
+Page.Metrics.ConfigurationSaveDuration.Title=Configuration Save Duration
+
View.Search.Button.Search=Szukaj
View.CreateSone.Text.WotIdentityRequired=Żeby utworzyć Sone potrzebujesz tożsamości z {link} wtyczki Sieć Zaufania{/link}.
Notification.SoneIsInserting.Text=Twoje Sone sone://{0} jest w tej chili wysyłane.
Notification.SoneIsInserted.Text=Twoje sone://{0} zostało wysłane w {1,number} {1,choice,0#seconds|1#second|1<seconds}.
Notification.SoneInsertAborted.Text=Twoje Sone sone://{0} nie mogło zostać wysłane.
-# 55-61, 465
+# 55-61, 334–338, 471
Page.Rescue.Text.Fetched=Восстановитель Sone скачал редакцию {0} вашего Sone. Пожалуйста проверьте свои сообщения, ответы и профиль.
Page.Rescue.Text.FetchedLast=Восстановитель Sone скачал последнюю доступную редакцию. Если ему не удалось восстановить ваш Sone, у вас кончились варианты действий.
Page.Rescue.Text.NotFetched=Восстановитель Sone не смог скачать редакцию {0} вашего Sone. Пожалуйста, либо попытайтесь снова с редакцией {0}, либо попробуйте более старую редакцию.
-Page.Rescue.Label.NextEdition=Следующая редакция:
+Page.Rescue.Label.NextEdition=Следующая редакция
Page.Rescue.Button.Fetch=Загрузить редакцию
Page.NoPermission.Title=Неавторизованный доступ - Sone
Page.Invalid.Page.Title=Произведено неверное действие
Page.Invalid.Text=Было произведено неверное действие, или верное действие с неверными параметрами. Пожалуйста, вернитесь на {link}главную страницу{/link} и попытайтесь снова. Если ошибка повторяется, вы, вероятно, обнаружили баг.
+Page.Metrics.Title=Metrics
+Page.Metrics.Page.Title=Metrics
+Page.Metrics.SoneInsertDuration.Title=Sone Insert Duration
+Page.Metrics.SoneParseDuration.Title=Sone Parse Duration
+Page.Metrics.ConfigurationSaveDuration.Title=Configuration Save Duration
+
View.Search.Button.Search=Поиск
View.CreateSone.Text.WotIdentityRequired=Чтобы создать Sone, вам необходима личность от дополнения {link}Web of Trust{/link}.
Notification.SoneIsInserting.Text=Your Sone sone://{0} is now being inserted.
Notification.SoneIsInserted.Text=Your Sone sone://{0} has been inserted in {1,number} {1,choice,0#seconds|1#second|1<seconds}.
Notification.SoneInsertAborted.Text=Your Sone sone://{0} could not be inserted.
-# 55-61, 67, 107, 127-128, 315-317, 319-321, 465, 471-473
+# 55-61, 67, 107, 127-128, 315-317, 319-321, 334–338, 471, 477-479
font-weight: bold;
}
-#sone .post .trust button {
- color: rgb(0, 128, 0);
-}
-
-#sone .post .distrust button {
- color: rgb(255, 0, 0);
-}
-
#sone .post .untrust button {
- color: rgb(64, 64, 64);
+ color: rgb(255, 0, 0);
}
#sone .post .delete button:hover, #sone .post .like button:hover, #sone .post .unlike button:hover, #sone .post .trust button:hover, #sone .post .distrust button:hover, #sone .post .untrust button:hover, #sone .post .bookmark button:hover, #sone .post .unbookmark button:hover {
#sone form#options li {
list-style-type: none;
}
+
+#sone table thead tr {
+ font-weight: bold;
+}
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
<input type="hidden" name="returnPage" value="<% request.uri|html>" />
<input type="hidden" name="sone" value="<% post.sone.id|html>" />
- <button type="submit" title="<%= View.Trust.Tooltip.Trust|l10n|html>">✓</button>
+ <button type="submit" title="<%= View.Trust.Tooltip.Trust|l10n|html>">👍</button>
</form>
<form class="distrust post-distrust<%if post.sone.trust.assigned> hidden<%/if>" action="distrust.html" method="post">
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
<input type="hidden" name="returnPage" value="<% request.uri|html>" />
<input type="hidden" name="sone" value="<% post.sone.id|html>" />
- <button type="submit" title="<%= View.Trust.Tooltip.Distrust|l10n|html>">✗</button>
+ <button type="submit" title="<%= View.Trust.Tooltip.Distrust|l10n|html>">👎</button>
</form>
<form class="untrust post-untrust<%if !post.sone.trust.assigned> hidden<%/if>" action="untrust.html" method="post">
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
<input type="hidden" name="returnPage" value="<% request.uri|html>" />
<input type="hidden" name="sone" value="<% post.sone.id|html>" />
- <button type="submit" title="<%= View.Trust.Tooltip.Untrust|l10n|html>">â\86¶</button>
+ <button type="submit" title="<%= View.Trust.Tooltip.Untrust|l10n|html>">â\9c\97</button>
</form>
<%/if>
<%/if>
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
<input type="hidden" name="returnPage" value="<% request.uri|html>" />
<input type="hidden" name="sone" value="<% reply.sone.id|html>" />
- <button type="submit" title="<%= View.Trust.Tooltip.Trust|l10n|html>">✓</button>
+ <button type="submit" title="<%= View.Trust.Tooltip.Trust|l10n|html>">👍</button>
</form>
<form class="distrust reply-distrust<%if reply.sone.trust.assigned> hidden<%/if>" action="distrust.html" method="post">
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
<input type="hidden" name="returnPage" value="<% request.uri|html>" />
<input type="hidden" name="sone" value="<% reply.sone.id|html>" />
- <button type="submit" title="<%= View.Trust.Tooltip.Distrust|l10n|html>">✗</button>
+ <button type="submit" title="<%= View.Trust.Tooltip.Distrust|l10n|html>">👎</button>
</form>
<form class="untrust reply-untrust<%if !reply.sone.trust.assigned> hidden<%/if>" action="untrust.html" method="post">
<input type="hidden" name="formPassword" value="<% formPassword|html>" />
<input type="hidden" name="returnPage" value="<% request.uri|html>" />
<input type="hidden" name="sone" value="<% reply.sone.id|html>" />
- <button type="submit" title="<%= View.Trust.Tooltip.Untrust|l10n|html>">â\86¶</button>
+ <button type="submit" title="<%= View.Trust.Tooltip.Untrust|l10n|html>">â\9c\97</button>
</form>
<%/if>
<%/if>
<thead>
<tr>
<td>Metric</td>
- <td>Count</td>
- <td>Min</td>
- <td>Max</td>
- <td>Mean</td>
- <td>Median</td>
- <td>75%</td>
- <td>95%</td>
- <td>98%</td>
- <td>99%</td>
- <td>99.9%</td>
+ <td class="numeric">Count</td>
+ <td class="numeric">Min</td>
+ <td class="numeric">Max</td>
+ <td class="numeric">Mean</td>
+ <td class="numeric">Median</td>
+ <td class="numeric">75%</td>
+ <td class="numeric">95%</td>
+ <td class="numeric">98%</td>
+ <td class="numeric">99%</td>
+ <td class="numeric">99.9%</td>
</tr>
</thead>
<tbody>
- <tr>
- <td><%= Page.Metrics.SoneInsertDuration.Title|l10n|html></td>
- <td class="numeric"><% soneInsertDurationCount|html></td>
- <td class="numeric"><% soneInsertDurationMin|duration scale=="μs"|html></td>
- <td class="numeric"><% soneInsertDurationMax|duration scale=="μs"|html></td>
- <td class="numeric"><% soneInsertDurationMean|duration scale=="μs"|html></td>
- <td class="numeric"><% soneInsertDurationMedian|duration scale=="μs"|html></td>
- <td class="numeric"><% soneInsertDurationPercentile75|duration scale=="μs"|html></td>
- <td class="numeric"><% soneInsertDurationPercentile95|duration scale=="μs"|html></td>
- <td class="numeric"><% soneInsertDurationPercentile98|duration scale=="μs"|html></td>
- <td class="numeric"><% soneInsertDurationPercentile99|duration scale=="μs"|html></td>
- <td class="numeric"><% soneInsertDurationPercentile999|duration scale=="μs"|html></td>
- </tr>
- <tr>
- <td><%= Page.Metrics.SoneParsingDuration.Title|l10n|html></td>
- <td class="numeric"><% soneParsingDurationCount|html></td>
- <td class="numeric"><% soneParsingDurationMin|duration scale=="μs"|html></td>
- <td class="numeric"><% soneParsingDurationMax|duration scale=="μs"|html></td>
- <td class="numeric"><% soneParsingDurationMean|duration scale=="μs"|html></td>
- <td class="numeric"><% soneParsingDurationMedian|duration scale=="μs"|html></td>
- <td class="numeric"><% soneParsingDurationPercentile75|duration scale=="μs"|html></td>
- <td class="numeric"><% soneParsingDurationPercentile95|duration scale=="μs"|html></td>
- <td class="numeric"><% soneParsingDurationPercentile98|duration scale=="μs"|html></td>
- <td class="numeric"><% soneParsingDurationPercentile99|duration scale=="μs"|html></td>
- <td class="numeric"><% soneParsingDurationPercentile999|duration scale=="μs"|html></td>
- </tr>
+ <%foreach histograms histogram>
+ <% histogram.value|render-histogram name=histogram.key>
+ <%/foreach>
</tbody>
</table>
<%/if>
<form action="rescue.html" method="post">
<input type="hidden" name="formPassword" value="<%formPassword|html>" />
- <div>
- <%= Page.Rescue.Label.NextEdition|l10n|html>: <%soneRescuer.nextEdition>
- <button type="submit" name="fetch" value="true"><%= Page.Rescue.Button.Fetch|l10n|html></button>
- </div>
+ <label><%= Page.Rescue.Label.NextEdition|l10n|html></label>
+ <input type="field" name="edition" value="<%soneRescuer.nextEdition>" />
+ <button type="submit" name="fetch" value="true"><%= Page.Rescue.Button.Fetch|l10n|html></button>
</form>
<%else>
<%if soneRescuer.lastFetchSuccessful>
--- /dev/null
+package net.pterodactylus.sone.core;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import net.pterodactylus.sone.data.Sone;
+
+import freenet.keys.FreenetURI;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Unit test for {@link SoneRescuer}.
+ */
+public class SoneRescuerTest {
+
+ private static final long CURRENT_EDITION = 12L;
+ private static final long SOME_OTHER_EDITION = 15L;
+ private final Core core = mock(Core.class);
+ private final SoneDownloader soneDownloader = mock(SoneDownloader.class);
+ private final Sone sone = mock(Sone.class);
+ private SoneRescuer soneRescuer;
+
+ @Before
+ public void setupSone() {
+ FreenetURI soneUri = mock(FreenetURI.class);
+ when(soneUri.getEdition()).thenReturn(CURRENT_EDITION);
+ when(sone.getRequestUri()).thenReturn(soneUri);
+ }
+
+ @Before
+ public void setupSoneRescuer() {
+ soneRescuer = new SoneRescuer(core, soneDownloader, sone);
+ }
+
+ @Test
+ public void newSoneRescuerIsNotFetchingAnything() {
+ assertThat(soneRescuer.isFetching(), is(false));
+ }
+
+ @Test
+ public void newSoneRescuerStartsAtCurrentEditionOfSone() {
+ assertThat(soneRescuer.getCurrentEdition(), is(CURRENT_EDITION));
+ }
+
+ @Test
+ public void newSoneRescuerHasANextEditionToGet() {
+ assertThat(soneRescuer.hasNextEdition(), is(true));
+ }
+
+ @Test
+ public void soneRescuerDoesNotHaveANextEditionIfCurrentEditionIsZero() {
+ when(sone.getRequestUri().getEdition()).thenReturn(0L);
+ soneRescuer = new SoneRescuer(core, soneDownloader, sone);
+ assertThat(soneRescuer.hasNextEdition(), is(false));
+ }
+
+ @Test
+ public void nextEditionIsOneSmallerThanTheCurrentEdition() {
+ assertThat(soneRescuer.getNextEdition(), is(CURRENT_EDITION - 1));
+ }
+
+ @Test
+ public void currentEditionCanBeSet() {
+ soneRescuer.setEdition(SOME_OTHER_EDITION);
+ assertThat(soneRescuer.getCurrentEdition(), is(SOME_OTHER_EDITION));
+ }
+
+ @Test
+ public void lastFetchOfANewSoneRescuerWasSuccessful() {
+ assertThat(soneRescuer.isLastFetchSuccessful(), is(true));
+ }
+
+ @Test
+ public void mainLoopStopsWhenItShould() {
+ soneRescuer.stop();
+ soneRescuer.serviceRun();
+ }
+
+ @Test
+ public void successfulInsert() {
+ final Sone fetchedSone = mock(Sone.class);
+ returnUriOnInsert(fetchedSone);
+ soneRescuer.startNextFetch();
+ soneRescuer.serviceRun();
+ verify(core).lockSone(eq(sone));
+ verify(core).updateSone(eq(fetchedSone), eq(true));
+ assertThat(soneRescuer.isLastFetchSuccessful(), is(true));
+ assertThat(soneRescuer.isFetching(), is(false));
+ }
+
+ @Test
+ public void nonSuccessfulInsertIsRecognized() {
+ returnUriOnInsert(null);
+ soneRescuer.startNextFetch();
+ soneRescuer.serviceRun();
+ verify(core).lockSone(eq(sone));
+ verify(core, never()).updateSone(any(Sone.class), eq(true));
+ assertThat(soneRescuer.isLastFetchSuccessful(), is(false));
+ assertThat(soneRescuer.isFetching(), is(false));
+ }
+
+ private void returnUriOnInsert(final Sone fetchedSone) {
+ FreenetURI keyWithMetaStrings = setupFreenetUri();
+ doAnswer(new Answer<Sone>() {
+ @Override
+ public Sone answer(InvocationOnMock invocation) throws Throwable {
+ soneRescuer.stop();
+ return fetchedSone;
+ }
+ }).when(soneDownloader).fetchSone(eq(sone), eq(keyWithMetaStrings), eq(true));
+ }
+
+ private FreenetURI setupFreenetUri() {
+ FreenetURI sskKey = mock(FreenetURI.class);
+ FreenetURI keyWithDocName = mock(FreenetURI.class);
+ FreenetURI keyWithMetaStrings = mock(FreenetURI.class);
+ when(keyWithDocName.setMetaString(eq(new String[] { "sone.xml" }))).thenReturn(keyWithMetaStrings);
+ when(sskKey.setDocName(eq("Sone-" + CURRENT_EDITION))).thenReturn(keyWithDocName);
+ when(sone.getRequestUri().setKeyType(eq("SSK"))).thenReturn(sskKey);
+ return keyWithMetaStrings;
+ }
+
+}
+++ /dev/null
-package net.pterodactylus.sone.freenet;
-
-import static freenet.support.Base64.encode;
-import static net.pterodactylus.sone.freenet.Key.from;
-import static net.pterodactylus.sone.freenet.Key.routingKey;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-
-import java.net.MalformedURLException;
-
-import freenet.keys.FreenetURI;
-
-import org.junit.Test;
-
-/**
- * Unit test for {@link Key}.
- */
-public class KeyTest {
-
- private final FreenetURI uri;
- private final Key key;
-
- public KeyTest() throws MalformedURLException {
- uri = new FreenetURI(
- "SSK@NfUYvxDwU9vqb2mh-qdT~DYJ6U0XNbxMGGoLe0aCHJs,Miglsgix0VR56ZiPl4NgjnUd~UdrnHqIvXJ3KKHmxmI,AQACAAE/some-site-12/foo/bar.html");
- key = from(uri);
- }
-
- @Test
- public void keyCanBeCreatedFromFreenetUri() throws MalformedURLException {
- assertThat(key.getRoutingKey(),
- is("NfUYvxDwU9vqb2mh-qdT~DYJ6U0XNbxMGGoLe0aCHJs"));
- assertThat(key.getCryptoKey(),
- is("Miglsgix0VR56ZiPl4NgjnUd~UdrnHqIvXJ3KKHmxmI"));
- assertThat(key.getExtra(), is("AQACAAE"));
- }
-
- @Test
- public void keyCanBeConvertedToUsk() throws MalformedURLException {
- FreenetURI uskUri = key.toUsk("other-site", 15, "some", "path.html");
- assertThat(uskUri.toString(),
- is("USK@NfUYvxDwU9vqb2mh-qdT~DYJ6U0XNbxMGGoLe0aCHJs,Miglsgix0VR56ZiPl4NgjnUd~UdrnHqIvXJ3KKHmxmI,AQACAAE/other-site/15/some/path.html"));
- }
-
- @Test
- public void keyCanBeConvertedToSskWithoutEdition()
- throws MalformedURLException {
- FreenetURI uskUri = key.toSsk("other-site", "some", "path.html");
- assertThat(uskUri.toString(),
- is("SSK@NfUYvxDwU9vqb2mh-qdT~DYJ6U0XNbxMGGoLe0aCHJs,Miglsgix0VR56ZiPl4NgjnUd~UdrnHqIvXJ3KKHmxmI,AQACAAE/other-site/some/path.html"));
- }
-
- @Test
- public void keyCanBeConvertedToSskWithEdition()
- throws MalformedURLException {
- FreenetURI uskUri = key.toSsk("other-site", 15, "some", "path.html");
- assertThat(uskUri.toString(),
- is("SSK@NfUYvxDwU9vqb2mh-qdT~DYJ6U0XNbxMGGoLe0aCHJs,Miglsgix0VR56ZiPl4NgjnUd~UdrnHqIvXJ3KKHmxmI,AQACAAE/other-site-15/some/path.html"));
- }
-
- @Test
- public void routingKeyIsExtractCorrectly() {
- assertThat(routingKey(uri),
- is("NfUYvxDwU9vqb2mh-qdT~DYJ6U0XNbxMGGoLe0aCHJs"));
- }
-
-}
+++ /dev/null
-/*
- * Sone - DefaultIdentityTest.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import static com.google.common.collect.ImmutableMap.of;
-import static java.util.Arrays.asList;
-import static net.pterodactylus.sone.test.Matchers.matchesRegex;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.empty;
-import static org.hamcrest.Matchers.hasEntry;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.hamcrest.Matchers.nullValue;
-import static org.hamcrest.collection.IsIterableContainingInOrder.contains;
-import static org.mockito.Mockito.mock;
-
-import java.util.Collections;
-
-import org.junit.Test;
-
-/**
- * Unit test for {@link DefaultIdentity}.
- */
-public class DefaultIdentityTest {
-
- protected final DefaultIdentity identity = createIdentity();
-
- protected DefaultIdentity createIdentity() {
- return new DefaultIdentity("Id", "Nickname", "RequestURI");
- }
-
- @Test
- public void identityCanBeCreated() {
- assertThat(identity.getId(), is("Id"));
- assertThat(identity.getNickname(), is("Nickname"));
- assertThat(identity.getRequestUri(), is("RequestURI"));
- assertThat(identity.getContexts(), empty());
- assertThat(identity.getProperties(), is(Collections.<String, String>emptyMap()));
- }
-
- @Test
- public void contextsAreAddedCorrectly() {
- identity.addContext("Test");
- assertThat(identity.getContexts(), contains("Test"));
- assertThat(identity.hasContext("Test"), is(true));
- }
-
- @Test
- public void contextsAreRemovedCorrectly() {
- identity.addContext("Test");
- identity.removeContext("Test");
- assertThat(identity.getContexts(), empty());
- assertThat(identity.hasContext("Test"), is(false));
- }
-
- @Test
- public void contextsAreSetCorrectlyInBulk() {
- identity.addContext("Test");
- identity.setContexts(asList("Test1", "Test2"));
- assertThat(identity.getContexts(), containsInAnyOrder("Test1", "Test2"));
- assertThat(identity.hasContext("Test"), is(false));
- assertThat(identity.hasContext("Test1"), is(true));
- assertThat(identity.hasContext("Test2"), is(true));
- }
-
- @Test
- public void propertiesAreAddedCorrectly() {
- identity.setProperty("Key", "Value");
- assertThat(identity.getProperties().size(), is(1));
- assertThat(identity.getProperties(), hasEntry("Key", "Value"));
- assertThat(identity.getProperty("Key"), is("Value"));
- }
-
- @Test
- public void propertiesAreRemovedCorrectly() {
- identity.setProperty("Key", "Value");
- identity.removeProperty("Key");
- assertThat(identity.getProperties(), is(Collections.<String, String>emptyMap()));
- assertThat(identity.getProperty("Key"), nullValue());
- }
-
- @Test
- public void propertiesAreSetCorrectlyInBulk() {
- identity.setProperty("Key", "Value");
- identity.setProperties(of("Key1", "Value1", "Key2", "Value2"));
- assertThat(identity.getProperties().size(), is(2));
- assertThat(identity.getProperty("Key"), nullValue());
- assertThat(identity.getProperty("Key1"), is("Value1"));
- assertThat(identity.getProperty("Key2"), is("Value2"));
- }
-
- @Test
- public void trustRelationshipsAreAddedCorrectly() {
- OwnIdentity ownIdentity = mock(OwnIdentity.class);
- Trust trust = mock(Trust.class);
- identity.setTrust(ownIdentity, trust);
- assertThat(identity.getTrust(ownIdentity), is(trust));
- }
-
- @Test
- public void trustRelationshipsAreRemovedCorrectly() {
- OwnIdentity ownIdentity = mock(OwnIdentity.class);
- Trust trust = mock(Trust.class);
- identity.setTrust(ownIdentity, trust);
- identity.removeTrust(ownIdentity);
- assertThat(identity.getTrust(ownIdentity), nullValue());
- }
-
- @Test
- public void identitiesWithTheSameIdAreEqual() {
- DefaultIdentity identity2 = new DefaultIdentity("Id", "Nickname2", "RequestURI2");
- assertThat(identity2, is(identity));
- assertThat(identity, is(identity2));
- }
-
- @Test
- public void twoEqualIdentitiesHaveTheSameHashCode() {
- DefaultIdentity identity2 = new DefaultIdentity("Id", "Nickname2", "RequestURI2");
- assertThat(identity.hashCode(), is(identity2.hashCode()));
- }
-
- @Test
- public void nullDoesNotMatchAnIdentity() {
- assertThat(identity, not(is((Object) null)));
- }
-
- @Test
- public void toStringContainsIdAndNickname() {
- String identityString = identity.toString();
- assertThat(identityString, matchesRegex(".*\\bId\\b.*"));
- assertThat(identityString, matchesRegex(".*\\bNickname\\b.*"));
- }
-
-}
+++ /dev/null
-/*
- * Sone - DefaultOwnIdentityTest.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-
-import org.junit.Test;
-
-/**
- * Unit test for {@link DefaultOwnIdentity}.
- */
-public class DefaultOwnIdentityTest extends DefaultIdentityTest {
-
- @Override
- protected DefaultIdentity createIdentity() {
- return new DefaultOwnIdentity("Id", "Nickname", "RequestURI", "InsertURI");
- }
-
- @Test
- public void ownIdentityCanBeCreated() {
- assertThat(((OwnIdentity) identity).getInsertUri(), is("InsertURI"));
- }
-
-}
+++ /dev/null
-/*
- * Sone - Identities.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import java.util.Collection;
-import java.util.Map;
-
-/**
- * Creates {@link Identity}s and {@link OwnIdentity}s.
- */
-public class Identities {
-
- public static OwnIdentity createOwnIdentity(String id, Collection<String> contexts, Map<String, String> properties) {
- DefaultOwnIdentity ownIdentity = new DefaultOwnIdentity(id, "Nickname" + id, "Request" + id, "Insert" + id);
- setContextsAndPropertiesOnIdentity(ownIdentity, contexts, properties);
- return ownIdentity;
- }
-
- public static Identity createIdentity(String id, Collection<String> contexts, Map<String, String> properties) {
- DefaultIdentity identity = new DefaultIdentity(id, "Nickname" + id, "Request" + id);
- setContextsAndPropertiesOnIdentity(identity, contexts, properties);
- return identity;
- }
-
- private static void setContextsAndPropertiesOnIdentity(Identity identity, Collection<String> contexts, Map<String, String> properties) {
- identity.setContexts(contexts);
- identity.setProperties(properties);
- }
-
-}
+++ /dev/null
-/*
- * Sone - IdentityChangeDetectorTest.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import static com.google.common.collect.ImmutableMap.of;
-import static com.google.common.collect.Lists.newArrayList;
-import static java.util.Arrays.asList;
-import static net.pterodactylus.sone.freenet.wot.Identities.createIdentity;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.empty;
-
-import java.util.Collection;
-
-import net.pterodactylus.sone.freenet.wot.IdentityChangeDetector.IdentityProcessor;
-
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Unit test for {@link IdentityChangeDetector}.
- */
-public class IdentityChangeDetectorTest {
-
- private final IdentityChangeDetector identityChangeDetector = new IdentityChangeDetector(createOldIdentities());
- private final Collection<Identity> newIdentities = newArrayList();
- private final Collection<Identity> removedIdentities = newArrayList();
- private final Collection<Identity> changedIdentities = newArrayList();
- private final Collection<Identity> unchangedIdentities = newArrayList();
-
- @Before
- public void setup() {
- identityChangeDetector.onNewIdentity(new IdentityProcessor() {
- @Override
- public void processIdentity(Identity identity) {
- newIdentities.add(identity);
- }
- });
- identityChangeDetector.onRemovedIdentity(new IdentityProcessor() {
- @Override
- public void processIdentity(Identity identity) {
- removedIdentities.add(identity);
- }
- });
- identityChangeDetector.onChangedIdentity(new IdentityProcessor() {
- @Override
- public void processIdentity(Identity identity) {
- changedIdentities.add(identity);
- }
- });
- identityChangeDetector.onUnchangedIdentity(new IdentityProcessor() {
- @Override
- public void processIdentity(Identity identity) {
- unchangedIdentities.add(identity);
- }
- });
- }
-
- @Test
- public void noDifferencesAreDetectedWhenSendingTheOldIdentitiesAgain() {
- identityChangeDetector.detectChanges(createOldIdentities());
- assertThat(newIdentities, empty());
- assertThat(removedIdentities, empty());
- assertThat(changedIdentities, empty());
- assertThat(unchangedIdentities, containsInAnyOrder(createIdentity1(), createIdentity2(), createIdentity3()));
- }
-
- @Test
- public void detectThatAnIdentityWasRemoved() {
- identityChangeDetector.detectChanges(asList(createIdentity1(), createIdentity3()));
- assertThat(newIdentities, empty());
- assertThat(removedIdentities, containsInAnyOrder(createIdentity2()));
- assertThat(changedIdentities, empty());
- assertThat(unchangedIdentities, containsInAnyOrder(createIdentity1(), createIdentity3()));
- }
-
- @Test
- public void detectThatAnIdentityWasAdded() {
- identityChangeDetector.detectChanges(asList(createIdentity1(), createIdentity2(), createIdentity3(), createIdentity4()));
- assertThat(newIdentities, containsInAnyOrder(createIdentity4()));
- assertThat(removedIdentities, empty());
- assertThat(changedIdentities, empty());
- assertThat(unchangedIdentities, containsInAnyOrder(createIdentity1(), createIdentity2(), createIdentity3()));
- }
-
- @Test
- public void detectThatAContextWasRemoved() {
- Identity identity2 = createIdentity2();
- identity2.removeContext("Context C");
- identityChangeDetector.detectChanges(asList(createIdentity1(), identity2, createIdentity3()));
- assertThat(newIdentities, empty());
- assertThat(removedIdentities, empty());
- assertThat(changedIdentities, containsInAnyOrder(identity2));
- assertThat(unchangedIdentities, containsInAnyOrder(createIdentity1(), createIdentity3()));
- }
-
- @Test
- public void detectThatAContextWasAdded() {
- Identity identity2 = createIdentity2();
- identity2.addContext("Context C1");
- identityChangeDetector.detectChanges(asList(createIdentity1(), identity2, createIdentity3()));
- assertThat(newIdentities, empty());
- assertThat(removedIdentities, empty());
- assertThat(changedIdentities, containsInAnyOrder(identity2));
- assertThat(unchangedIdentities, containsInAnyOrder(createIdentity1(), createIdentity3()));
- }
-
- @Test
- public void detectThatAPropertyWasRemoved() {
- Identity identity1 = createIdentity1();
- identity1.removeProperty("Key A");
- identityChangeDetector.detectChanges(asList(identity1, createIdentity2(), createIdentity3()));
- assertThat(newIdentities, empty());
- assertThat(removedIdentities, empty());
- assertThat(changedIdentities, containsInAnyOrder(identity1));
- assertThat(unchangedIdentities, containsInAnyOrder(createIdentity2(), createIdentity3()));
- }
-
- @Test
- public void detectThatAPropertyWasAdded() {
- Identity identity3 = createIdentity3();
- identity3.setProperty("Key A", "Value A");
- identityChangeDetector.detectChanges(asList(createIdentity1(), createIdentity2(), identity3));
- assertThat(newIdentities, empty());
- assertThat(removedIdentities, empty());
- assertThat(changedIdentities, containsInAnyOrder(identity3));
- assertThat(unchangedIdentities, containsInAnyOrder(createIdentity1(), createIdentity2()));
- }
-
- @Test
- public void detectThatAPropertyWasChanged() {
- Identity identity3 = createIdentity3();
- identity3.setProperty("Key E", "Value F");
- identityChangeDetector.detectChanges(asList(createIdentity1(), createIdentity2(), identity3));
- assertThat(newIdentities, empty());
- assertThat(removedIdentities, empty());
- assertThat(changedIdentities, containsInAnyOrder(identity3));
- assertThat(unchangedIdentities, containsInAnyOrder(createIdentity1(), createIdentity2()));
- }
-
- @Test
- public void noRemovedIdentitiesAreDetectedWithoutAnIdentityProcessor() {
- identityChangeDetector.onRemovedIdentity(null);
- identityChangeDetector.detectChanges(asList(createIdentity1(), createIdentity3()));
- }
-
- @Test
- public void noAddedIdentitiesAreDetectedWithoutAnIdentityProcessor() {
- identityChangeDetector.onNewIdentity(null);
- identityChangeDetector.detectChanges(asList(createIdentity1(), createIdentity2(), createIdentity3(), createIdentity4()));
- }
-
- private static Collection<Identity> createOldIdentities() {
- return asList(createIdentity1(), createIdentity2(), createIdentity3());
- }
-
- private static Identity createIdentity1() {
- return createIdentity("Test1", asList("Context A", "Context B"), of("Key A", "Value A", "Key B", "Value B"));
- }
-
- private static Identity createIdentity2() {
- return createIdentity("Test2", asList("Context C", "Context D"), of("Key C", "Value C", "Key D", "Value D"));
- }
-
- private static Identity createIdentity3() {
- return createIdentity("Test3", asList("Context E", "Context F"), of("Key E", "Value E", "Key F", "Value F"));
- }
-
- private static Identity createIdentity4() {
- return createIdentity("Test4", asList("Context G", "Context H"), of("Key G", "Value G", "Key H", "Value H"));
- }
-
-}
+++ /dev/null
-/*
- * Sone - IdentityChangeEventSenderTest.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import static com.google.common.collect.ImmutableMap.of;
-import static java.util.Arrays.asList;
-import static net.pterodactylus.sone.freenet.wot.Identities.createIdentity;
-import static net.pterodactylus.sone.freenet.wot.Identities.createOwnIdentity;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import net.pterodactylus.sone.freenet.wot.event.IdentityAddedEvent;
-import net.pterodactylus.sone.freenet.wot.event.IdentityRemovedEvent;
-import net.pterodactylus.sone.freenet.wot.event.IdentityUpdatedEvent;
-import net.pterodactylus.sone.freenet.wot.event.OwnIdentityAddedEvent;
-import net.pterodactylus.sone.freenet.wot.event.OwnIdentityRemovedEvent;
-
-import com.google.common.eventbus.EventBus;
-import org.junit.Test;
-
-/**
- * Unit test for {@link IdentityChangeEventSender}.
- */
-public class IdentityChangeEventSenderTest {
-
- private final EventBus eventBus = mock(EventBus.class);
- private final List<OwnIdentity> ownIdentities = asList(
- createOwnIdentity("O1", asList("Test"), of("KeyA", "ValueA")),
- createOwnIdentity("O2", asList("Test2"), of("KeyB", "ValueB")),
- createOwnIdentity("O3", asList("Test3"), of("KeyC", "ValueC"))
- );
- private final List<Identity> identities = asList(
- createIdentity("I1", Collections.<String>emptyList(), Collections.<String, String>emptyMap()),
- createIdentity("I2", Collections.<String>emptyList(), Collections.<String, String>emptyMap()),
- createIdentity("I3", Collections.<String>emptyList(), Collections.<String, String>emptyMap()),
- createIdentity("I2", asList("Test"), Collections.<String, String>emptyMap())
- );
- private final IdentityChangeEventSender identityChangeEventSender = new IdentityChangeEventSender(eventBus, createOldIdentities());
-
- @Test
- public void addingAnOwnIdentityIsDetectedAndReportedCorrectly() {
- Map<OwnIdentity, Collection<Identity>> newIdentities = createNewIdentities();
- identityChangeEventSender.detectChanges(newIdentities);
- verify(eventBus).post(eq(new OwnIdentityRemovedEvent(ownIdentities.get(0))));
- verify(eventBus).post(eq(new IdentityRemovedEvent(ownIdentities.get(0), identities.get(0))));
- verify(eventBus).post(eq(new IdentityRemovedEvent(ownIdentities.get(0), identities.get(1))));
- verify(eventBus).post(eq(new OwnIdentityAddedEvent(ownIdentities.get(2))));
- verify(eventBus).post(eq(new IdentityAddedEvent(ownIdentities.get(2), identities.get(1))));
- verify(eventBus).post(eq(new IdentityAddedEvent(ownIdentities.get(2), identities.get(2))));
- verify(eventBus).post(eq(new IdentityRemovedEvent(ownIdentities.get(1), identities.get(0))));
- verify(eventBus).post(eq(new IdentityAddedEvent(ownIdentities.get(1), identities.get(2))));
- verify(eventBus).post(eq(new IdentityUpdatedEvent(ownIdentities.get(1), identities.get(1))));
- }
-
- private Map<OwnIdentity, Collection<Identity>> createNewIdentities() {
- Map<OwnIdentity, Collection<Identity>> oldIdentities = new HashMap<>();
- oldIdentities.put(ownIdentities.get(1), asList(identities.get(3), identities.get(2)));
- oldIdentities.put(ownIdentities.get(2), asList(identities.get(1), identities.get(2)));
- return oldIdentities;
- }
-
- private Map<OwnIdentity, Collection<Identity>> createOldIdentities() {
- Map<OwnIdentity, Collection<Identity>> oldIdentities = new HashMap<>();
- oldIdentities.put(ownIdentities.get(0), asList(identities.get(0), identities.get(1)));
- oldIdentities.put(ownIdentities.get(1), asList(identities.get(0), identities.get(1)));
- return oldIdentities;
- }
-
-}
+++ /dev/null
-/*
- * Sone - IdentityLoaderTest.java - Copyright © 2013–2019 David Roden
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
-
-package net.pterodactylus.sone.freenet.wot;
-
-import static com.google.common.base.Optional.of;
-import static com.google.common.collect.Lists.newArrayList;
-import static com.google.common.collect.Sets.newHashSet;
-import static java.util.Arrays.asList;
-import static java.util.Collections.emptySet;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.hamcrest.Matchers.hasSize;
-import static org.hamcrest.Matchers.is;
-import static org.mockito.ArgumentMatchers.any;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-
-import java.util.Collection;
-import java.util.Collections;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-import com.google.common.base.Optional;
-import com.google.common.collect.ImmutableMap;
-import org.hamcrest.Matchers;
-import org.junit.Before;
-import org.junit.Test;
-
-/**
- * Unit test for {@link IdentityLoader}.
- */
-public class IdentityLoaderTest {
-
- private final WebOfTrustConnector webOfTrustConnector = mock(WebOfTrustConnector.class);
- private final IdentityLoader identityLoader = new IdentityLoader(webOfTrustConnector, of(new Context("Test")));
- private final IdentityLoader identityLoaderWithoutContext = new IdentityLoader(webOfTrustConnector);
-
- @Before
- public void setup() throws WebOfTrustException {
- List<OwnIdentity> ownIdentities = createOwnIdentities();
- when(webOfTrustConnector.loadAllOwnIdentities()).thenReturn(newHashSet(ownIdentities));
- when(webOfTrustConnector.loadTrustedIdentities(eq(ownIdentities.get(0)), any(Optional.class))).thenReturn(createTrustedIdentitiesForFirstOwnIdentity());
- when(webOfTrustConnector.loadTrustedIdentities(eq(ownIdentities.get(1)), any(Optional.class))).thenReturn(createTrustedIdentitiesForSecondOwnIdentity());
- when(webOfTrustConnector.loadTrustedIdentities(eq(ownIdentities.get(2)), any(Optional.class))).thenReturn(createTrustedIdentitiesForThirdOwnIdentity());
- when(webOfTrustConnector.loadTrustedIdentities(eq(ownIdentities.get(3)), any(Optional.class))).thenReturn(createTrustedIdentitiesForFourthOwnIdentity());
- }
-
- private List<OwnIdentity> createOwnIdentities() {
- return newArrayList(
- createOwnIdentity("O1", "ON1", "OR1", "OI1", asList("Test", "Test2"), ImmutableMap.of("KeyA", "ValueA", "KeyB", "ValueB")),
- createOwnIdentity("O2", "ON2", "OR2", "OI2", asList("Test"), ImmutableMap.of("KeyC", "ValueC")),
- createOwnIdentity("O3", "ON3", "OR3", "OI3", asList("Test2"), ImmutableMap.of("KeyE", "ValueE", "KeyD", "ValueD")),
- createOwnIdentity("O4", "ON4", "OR$", "OI4", asList("Test"), ImmutableMap.of("KeyA", "ValueA", "KeyD", "ValueD"))
- );
- }
-
- private Set<Identity> createTrustedIdentitiesForFirstOwnIdentity() {
- return newHashSet(
- createIdentity("I11", "IN11", "IR11", asList("Test"), ImmutableMap.of("KeyA", "ValueA"))
- );
- }
-
- private Set<Identity> createTrustedIdentitiesForSecondOwnIdentity() {
- return newHashSet(
- createIdentity("I21", "IN21", "IR21", asList("Test", "Test2"), ImmutableMap.of("KeyB", "ValueB"))
- );
- }
-
- private Set<Identity> createTrustedIdentitiesForThirdOwnIdentity() {
- return newHashSet(
- createIdentity("I31", "IN31", "IR31", asList("Test", "Test3"), ImmutableMap.of("KeyC", "ValueC"))
- );
- }
-
- private Set<Identity> createTrustedIdentitiesForFourthOwnIdentity() {
- return emptySet();
- }
-
- private OwnIdentity createOwnIdentity(String id, String nickname, String requestUri, String insertUri, List<String> contexts, ImmutableMap<String, String> properties) {
- OwnIdentity ownIdentity = new DefaultOwnIdentity(id, nickname, requestUri, insertUri);
- ownIdentity.setContexts(contexts);
- ownIdentity.setProperties(properties);
- return ownIdentity;
- }
-
- private Identity createIdentity(String id, String nickname, String requestUri, List<String> contexts, ImmutableMap<String, String> properties) {
- Identity identity = new DefaultIdentity(id, nickname, requestUri);
- identity.setContexts(contexts);
- identity.setProperties(properties);
- return identity;
- }
-
- @Test
- public void loadingIdentities() throws WebOfTrustException {
- List<OwnIdentity> ownIdentities = createOwnIdentities();
- Map<OwnIdentity, Collection<Identity>> identities = identityLoader.loadIdentities();
- verify(webOfTrustConnector).loadAllOwnIdentities();
- verify(webOfTrustConnector).loadTrustedIdentities(eq(ownIdentities.get(0)), eq(of("Test")));
- verify(webOfTrustConnector).loadTrustedIdentities(eq(ownIdentities.get(1)), eq(of("Test")));
- verify(webOfTrustConnector, never()).loadTrustedIdentities(eq(ownIdentities.get(2)), any(Optional.class));
- verify(webOfTrustConnector).loadTrustedIdentities(eq(ownIdentities.get(3)), eq(of("Test")));
- assertThat(identities.keySet(), hasSize(4));
- assertThat(identities.keySet(), containsInAnyOrder(ownIdentities.get(0), ownIdentities.get(1), ownIdentities.get(2), ownIdentities.get(3)));
- verifyIdentitiesForOwnIdentity(identities, ownIdentities.get(0), createTrustedIdentitiesForFirstOwnIdentity());
- verifyIdentitiesForOwnIdentity(identities, ownIdentities.get(1), createTrustedIdentitiesForSecondOwnIdentity());
- verifyIdentitiesForOwnIdentity(identities, ownIdentities.get(2), Collections.<Identity>emptySet());
- verifyIdentitiesForOwnIdentity(identities, ownIdentities.get(3), createTrustedIdentitiesForFourthOwnIdentity());
- }
-
- @Test
- public void loadingIdentitiesWithoutContext() throws WebOfTrustException {
- List<OwnIdentity> ownIdentities = createOwnIdentities();
- Map<OwnIdentity, Collection<Identity>> identities = identityLoaderWithoutContext.loadIdentities();
- verify(webOfTrustConnector).loadAllOwnIdentities();
- verify(webOfTrustConnector).loadTrustedIdentities(eq(ownIdentities.get(0)), eq(Optional.<String>absent()));
- verify(webOfTrustConnector).loadTrustedIdentities(eq(ownIdentities.get(1)), eq(Optional.<String>absent()));
- verify(webOfTrustConnector).loadTrustedIdentities(eq(ownIdentities.get(2)), eq(Optional.<String>absent()));
- verify(webOfTrustConnector).loadTrustedIdentities(eq(ownIdentities.get(3)), eq(Optional.<String>absent()));
- assertThat(identities.keySet(), hasSize(4));
- OwnIdentity firstOwnIdentity = ownIdentities.get(0);
- OwnIdentity secondOwnIdentity = ownIdentities.get(1);
- OwnIdentity thirdOwnIdentity = ownIdentities.get(2);
- OwnIdentity fourthOwnIdentity = ownIdentities.get(3);
- assertThat(identities.keySet(), containsInAnyOrder(firstOwnIdentity, secondOwnIdentity, thirdOwnIdentity, fourthOwnIdentity));
- verifyIdentitiesForOwnIdentity(identities, firstOwnIdentity, createTrustedIdentitiesForFirstOwnIdentity());
- verifyIdentitiesForOwnIdentity(identities, secondOwnIdentity, createTrustedIdentitiesForSecondOwnIdentity());
- verifyIdentitiesForOwnIdentity(identities, thirdOwnIdentity, createTrustedIdentitiesForThirdOwnIdentity());
- verifyIdentitiesForOwnIdentity(identities, fourthOwnIdentity, createTrustedIdentitiesForFourthOwnIdentity());
- }
-
- private void verifyIdentitiesForOwnIdentity(Map<OwnIdentity, Collection<Identity>> identities, OwnIdentity ownIdentity, Set<Identity> trustedIdentities) {
- assertThat(identities.get(ownIdentity), Matchers.<Collection<Identity>>is(trustedIdentities));
- }
-
-}
+++ /dev/null
-package net.pterodactylus.sone.freenet.wot;
-
-import static com.google.common.base.Optional.of;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.mockito.Mockito.doThrow;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-import net.pterodactylus.sone.freenet.plugin.PluginException;
-
-import com.google.common.eventbus.EventBus;
-import org.junit.Test;
-
-/**
- * Unit test for {@link IdentityManagerImpl}.
- */
-public class IdentityManagerTest {
-
- private final EventBus eventBus = mock(EventBus.class);
- private final WebOfTrustConnector webOfTrustConnector = mock(WebOfTrustConnector.class);
- private final IdentityManager identityManager = new IdentityManagerImpl(eventBus, webOfTrustConnector, new IdentityLoader(webOfTrustConnector, of(new Context("Test"))));
-
- @Test
- public void identityManagerPingsWotConnector() throws PluginException {
- assertThat(identityManager.isConnected(), is(true));
- verify(webOfTrustConnector).ping();
- }
-
- @Test
- public void disconnectedWotConnectorIsRecognized() throws PluginException {
- doThrow(PluginException.class).when(webOfTrustConnector).ping();
- assertThat(identityManager.isConnected(), is(false));
- verify(webOfTrustConnector).ping();
- }
-
-}
--- /dev/null
+package net.pterodactylus.sone.freenet.wot
+
+import com.google.common.eventbus.*
+import net.pterodactylus.sone.freenet.plugin.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.mockito.Mockito.*
+
+/**
+ * Unit test for [IdentityManagerImpl].
+ */
+class IdentityManagerTest {
+
+ private val eventBus = mock<EventBus>()
+ private val webOfTrustConnector = mock<WebOfTrustConnector>()
+ private val identityManager = IdentityManagerImpl(eventBus, webOfTrustConnector, IdentityLoader(webOfTrustConnector, Context("Test")))
+
+ @Test
+ fun identityManagerPingsWotConnector() {
+ assertThat(identityManager.isConnected, equalTo(true))
+ verify(webOfTrustConnector).ping()
+ }
+
+ @Test
+ fun disconnectedWotConnectorIsRecognized() {
+ doThrow(PluginException::class.java).whenever(webOfTrustConnector).ping()
+ assertThat(identityManager.isConnected, equalTo(false))
+ verify(webOfTrustConnector).ping()
+ }
+
+}
+++ /dev/null
-package net.pterodactylus.sone.freenet.wot.event;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.mockito.Mockito.mock;
-
-import net.pterodactylus.sone.freenet.wot.Identity;
-import net.pterodactylus.sone.freenet.wot.OwnIdentity;
-
-import org.junit.Test;
-
-/**
- * Unit test for {@link IdentityEvent}.
- */
-public class IdentityEventTest {
-
- private final OwnIdentity ownIdentity = mock(OwnIdentity.class);
- private final Identity identity = mock(Identity.class);
- private final IdentityEvent identityEvent = createIdentityEvent(ownIdentity, identity);
-
- private IdentityEvent createIdentityEvent(final OwnIdentity ownIdentity, final Identity identity) {
- return new IdentityEvent(ownIdentity, identity) {
- };
- }
-
- @Test
- public void identityEventRetainsIdentities() {
- assertThat(identityEvent.ownIdentity(), is(ownIdentity));
- assertThat(identityEvent.identity(), is(identity));
- }
-
- @Test
- public void eventsWithTheSameIdentityHaveTheSameHashCode() {
- IdentityEvent secondIdentityEvent = createIdentityEvent(ownIdentity, identity);
- assertThat(identityEvent.hashCode(), is(secondIdentityEvent.hashCode()));
- }
-
- @Test
- public void eventsWithTheSameIdentitiesAreEqual() {
- IdentityEvent secondIdentityEvent = createIdentityEvent(ownIdentity, identity);
- assertThat(identityEvent, is(secondIdentityEvent));
- assertThat(secondIdentityEvent, is(identityEvent));
- }
-
- @Test
- public void nullDoesNotEqualIdentityEvent() {
- assertThat(identityEvent, not(is((Object) null)));
- }
-
-
-}
+++ /dev/null
-package net.pterodactylus.sone.freenet.wot.event;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.mockito.Mockito.mock;
-
-import net.pterodactylus.sone.freenet.wot.OwnIdentity;
-
-import org.junit.Test;
-
-/**
- * Unit test for {@link OwnIdentityEvent}.
- */
-public class OwnIdentityEventTest {
-
- private final OwnIdentity ownIdentity = mock(OwnIdentity.class);
- private final OwnIdentityEvent ownIdentityEvent = createOwnIdentityEvent(ownIdentity);
-
- @Test
- public void eventRetainsOwnIdentity() {
- assertThat(ownIdentityEvent.ownIdentity(), is(ownIdentity));
- }
-
- protected OwnIdentityEvent createOwnIdentityEvent(final OwnIdentity ownIdentity) {
- return new OwnIdentityEvent(ownIdentity) {
- };
- }
-
- @Test
- public void twoOwnIdentityEventsWithTheSameIdentityHaveTheSameHashCode() {
- OwnIdentityEvent secondOwnIdentityEvent = createOwnIdentityEvent(ownIdentity);
- assertThat(secondOwnIdentityEvent.hashCode(), is(ownIdentityEvent.hashCode()));
- }
-
- @Test
- public void ownIdentityEventDoesNotMatchNull() {
- assertThat(ownIdentityEvent, not(is((Object) null)));
- }
-
- @Test
- public void ownIdentityEventDoesNotMatchObjectWithADifferentClass() {
- assertThat(ownIdentityEvent, not(is(new Object())));
- }
-
-}
import freenet.clients.http.SessionManager;
import freenet.clients.http.ToadletContext;
-import freenet.l10n.BaseL10n;
import freenet.support.api.HTTPRequest;
import com.google.common.base.Charsets;
@Rule
public final TemporaryFolder temporaryFolder = new TemporaryFolder();
- private final BaseL10n l10n = mock(BaseL10n.class);
private final StringWriter stringWriter = new StringWriter();
private final TemplateContext templateContext = new TemplateContext();
private Loaders loaders;
HTTPRequest httpRequest = mock(HTTPRequest.class);
ToadletContext toadletContext = mock(ToadletContext.class);
SessionManager sessionManager = mock(SessionManager.class);
- FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager);
+ FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext, sessionManager);
OutputStream outputStream = new ByteArrayOutputStream();
Response response = new Response(outputStream);
page.handleRequest(request, response);
import freenet.clients.http.SessionManager;
import freenet.clients.http.ToadletContext;
-import freenet.l10n.BaseL10n;
import freenet.support.api.HTTPRequest;
import org.junit.Test;
*/
public class DefaultLoadersTest {
- private final BaseL10n l10n = mock(BaseL10n.class);
private final Loaders loaders = new DefaultLoaders();
private final StringWriter stringWriter = new StringWriter();
private final TemplateContext templateContext = new TemplateContext();
HTTPRequest httpRequest = mock(HTTPRequest.class);
ToadletContext toadletContext = mock(ToadletContext.class);
SessionManager sessionManager = mock(SessionManager.class);
- FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager);
+ FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext, sessionManager);
OutputStream outputStream = new ByteArrayOutputStream();
Response response = new Response(outputStream);
staticPage.handleRequest(request, response);
+++ /dev/null
-package net.pterodactylus.sone.notify;
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.emptyIterable;
-import static org.hamcrest.Matchers.is;
-import static org.hamcrest.Matchers.not;
-import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
-import static org.mockito.hamcrest.MockitoHamcrest.argThat;
-
-import java.util.Arrays;
-
-import net.pterodactylus.util.notify.NotificationListener;
-import net.pterodactylus.util.template.Template;
-import net.pterodactylus.util.template.TemplateContext;
-
-import org.hamcrest.Matchers;
-import org.junit.Test;
-
-/**
- * Unit test for {@link ListNotification}.
- */
-public class ListNotificationTest {
-
- private static final String ID = "notification-id";
- private static final String KEY = "element-key";
- private static final String OTHER_KEY = "other-key";
-
- private final Template template = mock(Template.class);
- private final TemplateContext templateInitialContext = mock(TemplateContext.class);
- private ListNotification<Object> listNotification;
-
- public ListNotificationTest() {
- when(template.getInitialContext()).thenReturn(templateInitialContext);
- listNotification = new ListNotification<>(ID, KEY, template);
- }
-
- @Test
- public void creatingAListNotificationSetsEmptyIterableOnElementKeyInTemplateContext() {
- verify(templateInitialContext).set(eq(KEY), argThat(emptyIterable()));
- }
-
- @Test
- public void newListNotificationHasNoElement() {
- assertThat(listNotification.getElements(), emptyIterable());
- }
-
- @Test
- public void newListNotificationIsEmpty() {
- assertThat(listNotification.isEmpty(), is(true));
- }
-
- @Test
- public void listNotificationRetainsSetElements() {
- listNotification.setElements(Arrays.asList("a", "b", "c"));
- assertThat(listNotification.getElements(), Matchers.<Object>contains("a", "b", "c"));
- }
-
- @Test
- public void listNotificationRetainsAddedElements() {
- listNotification.add("a");
- listNotification.add("b");
- listNotification.add("c");
- assertThat(listNotification.getElements(), Matchers.<Object>contains("a", "b", "c"));
- }
-
- @Test
- public void listNotificationRemovesCorrectElement() {
- listNotification.setElements(Arrays.asList("a", "b", "c"));
- listNotification.remove("b");
- assertThat(listNotification.getElements(), Matchers.<Object>contains("a", "c"));
- }
-
- @Test
- public void removingTheLastElementDismissesTheNotification() {
- NotificationListener notificationListener = mock(NotificationListener.class);
- listNotification.addNotificationListener(notificationListener);
- listNotification.add("a");
- listNotification.remove("a");
- verify(notificationListener).notificationDismissed(listNotification);
- }
-
- @Test
- public void dismissingTheListNotificationRemovesAllElements() {
- listNotification.setElements(Arrays.asList("a", "b", "c"));
- listNotification.dismiss();
- assertThat(listNotification.getElements(), emptyIterable());
- }
-
- @Test
- public void listNotificationWithDifferentElementsIsNotEqual() {
- ListNotification secondNotification = new ListNotification(ID, KEY, template);
- listNotification.add("a");
- secondNotification.add("b");
- assertThat(listNotification, not(is(secondNotification)));
- }
-
- @Test
- public void listNotificationWithDifferentKeyIsNotEqual() {
- ListNotification secondNotification = new ListNotification(ID, OTHER_KEY, template);
- assertThat(listNotification, not(is(secondNotification)));
- }
-
- @Test
- public void copiedNotificationsHaveTheSameHashCode() {
- ListNotification secondNotification = new ListNotification(listNotification);
- listNotification.add("a");
- secondNotification.add("a");
- listNotification.setLastUpdateTime(secondNotification.getLastUpdatedTime());
- assertThat(listNotification.hashCode(), is(secondNotification.hashCode()));
- }
-
- @Test
- public void listNotificationIsNotEqualToOtherObjects() {
- assertThat(listNotification, not(is(new Object())));
- }
-
-}
import java.io.IOException;
import java.io.InputStream;
+import javax.annotation.*;
+
import net.pterodactylus.sone.data.Album;
import net.pterodactylus.sone.data.Image;
import net.pterodactylus.sone.data.Post;
};
}
- public static Matcher<Post> isPost(String postId, long time,
- String text, Optional<String> recipient) {
+ public static Matcher<Post> isPost(String postId, long time, String text, @Nullable String recipient) {
return new PostMatcher(postId, time, text, recipient);
}
private final String postId;
private final long time;
private final String text;
- private final Optional<String> recipient;
+ @Nullable
+ private final String recipient;
- private PostMatcher(String postId, long time, String text,
- Optional<String> recipient) {
+ private PostMatcher(String postId, long time, String text, @Nullable String recipient) {
this.postId = postId;
this.time = time;
this.text = text;
.appendValue(text);
return false;
}
- if (recipient.isPresent()) {
+ if (recipient != null) {
if (!post.getRecipientId().isPresent()) {
mismatchDescription.appendText(
"Recipient not present");
return false;
}
- if (!post.getRecipientId().get().equals(recipient.get())) {
+ if (!post.getRecipientId().get().equals(recipient)) {
mismatchDescription.appendText("Recipient is not ")
- .appendValue(recipient.get());
+ .appendValue(recipient);
return false;
}
} else {
.appendValue(postId);
description.appendText(", created at @").appendValue(time);
description.appendText(", text ").appendValue(text);
- if (recipient.isPresent()) {
+ if (recipient != null) {
description.appendText(", directed at ")
- .appendValue(recipient.get());
+ .appendValue(recipient);
}
}
val postBuilderFactory = createPostBuilderFactory()
val posts = configurationSoneParser.parsePosts(postBuilderFactory)
assertThat(posts, containsInAnyOrder(
- isPost("P0", 1000L, "T0", absent()),
- isPost("P1", 1001L, "T1", of("1234567890123456789012345678901234567890123"))
+ isPost("P0", 1000L, "T0", null),
+ isPost("P1", 1001L, "T1", "1234567890123456789012345678901234567890123")
))
}
fun postWithInvalidRecipientIdIsRecognized() {
setupPostWithInvalidRecipientId()
val posts = configurationSoneParser.parsePosts(createPostBuilderFactory())
- assertThat(posts, contains(isPost("P0", 1000L, "T0", absent())))
+ assertThat(posts, contains(isPost("P0", 1000L, "T0", null)))
}
private fun setupPostWithInvalidRecipientId() {
import com.google.common.io.ByteStreams
import freenet.keys.FreenetURI
import net.pterodactylus.sone.core.FreenetInterface.BackgroundFetchCallback
-import net.pterodactylus.sone.test.capture
-import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.test.*
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.equalTo
import org.mockito.ArgumentMatchers.any
import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
import java.io.ByteArrayOutputStream
elementLoader.loadElement(IMAGE_ID)
verify(freenetInterface).startFetch(eq(freenetURI), callback.capture())
callback.value.failed(freenetURI)
- `when`(ticker.read()).thenReturn(TimeUnit.MINUTES.toNanos(31))
+ whenever(ticker.read()).thenReturn(TimeUnit.MINUTES.toNanos(31))
val linkedElement = elementLoader.loadElement(IMAGE_ID)
assertThat(linkedElement.failed, equalTo(false))
assertThat(linkedElement.loading, equalTo(true))
@Before
fun setupCallbackCaptorAndUskManager() {
- doNothing().`when`(uskManager).subscribe(any(USK::class.java), callbackCaptor.capture(), anyBoolean(), any(RequestClient::class.java))
+ doNothing().whenever(uskManager).subscribe(any(USK::class.java), callbackCaptor.capture(), anyBoolean(), any(RequestClient::class.java))
}
@Test
@Test
fun `exception when inserting image is ignored`() {
- doThrow(SoneException::class.java).`when`(freenetInterface).insertImage(eq(temporaryImage), eq(image), any(InsertToken::class.java))
+ doThrow(SoneException::class.java).whenever(freenetInterface).insertImage(eq(temporaryImage), eq(image), any(InsertToken::class.java))
imageInserter.insertImage(temporaryImage, image)
verify(freenetInterface).insertImage(eq(temporaryImage), eq(image), any(InsertToken::class.java))
}
doAnswer {
soneInserter.stop()
null
- }.`when`(core).touchConfiguration()
+ }.whenever(core).touchConfiguration()
soneInserter.serviceRun()
val soneEvents = ArgumentCaptor.forClass(SoneEvent::class.java)
verify(freenetInterface).insertDirectory(eq(insertUri), any<HashMap<String, Any>>(), eq("index.html"))
doAnswer {
soneInserter.stop()
null
- }.`when`(core).touchConfiguration()
+ }.whenever(core).touchConfiguration()
soneInserter.serviceRun()
val histogram = metricRegistry.histogram("sone.insert.duration")
assertThat(histogram.count, equalTo(1L))
fun `unsuccessful parsing does not add a histogram entry`() {
val inputStream = javaClass.getResourceAsStream("sone-parser-with-invalid-image-height.xml")
assertThat(soneParser.parseSone(sone, inputStream), nullValue())
- val histogram = metricRegistry.histogram("sone.parsing.duration")
+ val histogram = metricRegistry.histogram("sone.parse.duration")
assertThat(histogram.count, equalTo(0L))
}
fun `successful parsing adds histogram entry`() {
val inputStream = javaClass.getResourceAsStream("sone-parser-without-images.xml")
assertThat(soneParser.parseSone(sone, inputStream), notNullValue())
- val histogram = metricRegistry.histogram("sone.parsing.duration")
+ val histogram = metricRegistry.histogram("sone.parse.duration")
assertThat(histogram.count, equalTo(1L))
}
+++ /dev/null
-package net.pterodactylus.sone.core
-
-import freenet.keys.*
-import net.pterodactylus.sone.data.*
-import net.pterodactylus.sone.test.*
-import org.hamcrest.MatcherAssert.*
-import org.hamcrest.Matchers.equalTo
-import org.junit.*
-import org.mockito.ArgumentMatchers.any
-import org.mockito.ArgumentMatchers.eq
-import org.mockito.Mockito.doAnswer
-import org.mockito.Mockito.never
-import org.mockito.Mockito.verify
-
-/**
- * Unit test for [SoneRescuer].
- */
-class SoneRescuerTest {
-
- private val core = mock<Core>()
- private val soneDownloader = mock<SoneDownloader>()
- private val sone = mock<Sone>().apply {
- val soneUri = mock<FreenetURI>()
- whenever(soneUri.edition).thenReturn(currentEdition)
- whenever(requestUri).thenReturn(soneUri)
- }
- private val soneRescuer = SoneRescuer(core, soneDownloader, sone)
-
- @Test
- fun newSoneRescuerIsNotFetchingAnything() {
- assertThat(soneRescuer.isFetching, equalTo(false))
- }
-
- @Test
- fun newSoneRescuerStartsAtCurrentEditionOfSone() {
- assertThat(soneRescuer.currentEdition, equalTo(currentEdition))
- }
-
- @Test
- fun newSoneRescuerHasANextEditionToGet() {
- assertThat(soneRescuer.hasNextEdition(), equalTo(true))
- }
-
- @Test
- fun soneRescuerDoesNotHaveANextEditionIfCurrentEditionIsZero() {
- whenever(sone.requestUri.edition).thenReturn(0L)
- val soneRescuer = SoneRescuer(core, soneDownloader, sone)
- assertThat(soneRescuer.hasNextEdition(), equalTo(false))
- }
-
- @Test
- fun nextEditionIsOneSmallerThanTheCurrentEdition() {
- assertThat(soneRescuer.nextEdition, equalTo(currentEdition - 1))
- }
-
- @Test
- fun lastFetchOfANewSoneRescuerWasSuccessful() {
- assertThat(soneRescuer.isLastFetchSuccessful, equalTo(true))
- }
-
- @Test
- fun mainLoopStopsWhenItShould() {
- soneRescuer.stop()
- soneRescuer.serviceRun()
- }
-
- @Test
- fun successfulInsert() {
- val fetchedSone = mock<Sone>()
- returnUriOnInsert(fetchedSone)
- soneRescuer.startNextFetch()
- soneRescuer.serviceRun()
- verify(core).lockSone(eq(sone))
- verify(core).updateSone(eq(fetchedSone), eq(true))
- assertThat(soneRescuer.isLastFetchSuccessful, equalTo(true))
- assertThat(soneRescuer.isFetching, equalTo(false))
- assertThat(soneRescuer.currentEdition, equalTo(currentEdition - 1))
- }
-
- @Test
- fun nonSuccessfulInsertIsRecognized() {
- returnUriOnInsert(null)
- soneRescuer.startNextFetch()
- soneRescuer.serviceRun()
- verify(core).lockSone(eq(sone))
- verify(core, never()).updateSone(any(Sone::class.java), eq(true))
- assertThat(soneRescuer.isLastFetchSuccessful, equalTo(false))
- assertThat(soneRescuer.isFetching, equalTo(false))
- assertThat(soneRescuer.currentEdition, equalTo(currentEdition))
- }
-
- private fun returnUriOnInsert(fetchedSone: Sone?) {
- val keyWithMetaStrings = setupFreenetUri()
- doAnswer {
- soneRescuer.stop()
- fetchedSone
- }.`when`(soneDownloader).fetchSone(eq(sone), eq(keyWithMetaStrings), eq(true))
- }
-
- private fun setupFreenetUri(): FreenetURI {
- val sskKey = mock<FreenetURI>()
- val keyWithDocName = mock<FreenetURI>()
- val keyWithMetaStrings = mock<FreenetURI>()
- whenever(keyWithDocName.setMetaString(eq(arrayOf("sone.xml")))).thenReturn(keyWithMetaStrings)
- whenever(sskKey.setDocName(eq("Sone-" + (currentEdition - 1)))).thenReturn(keyWithDocName)
- whenever(sone.requestUri.setKeyType(eq("SSK"))).thenReturn(sskKey)
- return keyWithMetaStrings
- }
-
-}
-
-private const val currentEdition = 12L
@Test
fun `stored sone is made available`() {
storeSone()
- assertThat(memoryDatabase.getPost("post1"), isPost("post1", 1000L, "post1", absent()))
- assertThat(memoryDatabase.getPost("post2"), isPost("post2", 2000L, "post2", of(RECIPIENT_ID)))
+ assertThat(memoryDatabase.getPost("post1"), isPost("post1", 1000L, "post1", null))
+ assertThat(memoryDatabase.getPost("post2"), isPost("post2", 2000L, "post2", RECIPIENT_ID))
assertThat(memoryDatabase.getPost("post3"), nullValue())
assertThat(memoryDatabase.getPostReply("reply1"), isPostReply("reply1", "post1", 3000L, "reply1"))
assertThat(memoryDatabase.getPostReply("reply2"), isPostReply("reply2", "post2", 4000L, "reply2"))
verify(configuration.getStringValue("KnownPosts/0/ID"), times(1)).value = null
}
+ @Test
+ @Dirty("the rate limiter should be mocked")
+ fun `setting posts as knows twice in a row only saves the database once`() {
+ prepareConfigurationValues()
+ val post = mock<Post>()
+ whenever(post.id).thenReturn("post-id")
+ memoryDatabase.setPostKnown(post, true)
+ memoryDatabase.setPostKnown(post, true)
+ verify(configuration, times(1)).getStringValue("KnownPosts/1/ID")
+ }
+
+ @Test
+ @Dirty("the rate limiter should be mocked")
+ fun `setting post replies as knows twice in a row only saves the database once`() {
+ prepareConfigurationValues()
+ val postReply = mock<PostReply>()
+ whenever(postReply.id).thenReturn("post-reply-id")
+ memoryDatabase.setPostReplyKnown(postReply, true)
+ memoryDatabase.setPostReplyKnown(postReply, true)
+ verify(configuration, times(1)).getStringValue("KnownReplies/1/ID")
+ }
+
}
private const val SONE_ID = "sone"
--- /dev/null
+package net.pterodactylus.sone.freenet
+
+import freenet.l10n.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import java.util.*
+
+/**
+ * Test for [BaseL10nTranslation].
+ */
+class BaseL10nTranslationTest {
+
+ private val baseL10n = mock<BaseL10n>()
+ private val translation = BaseL10nTranslation(baseL10n)
+
+ @Test
+ fun `translate method is facade for the correct method`() {
+ whenever(baseL10n.getString("test")).thenReturn("answer")
+ assertThat(translation.translate("test"), equalTo("answer"))
+ }
+
+ @Test
+ fun `language exposes correct short code`() {
+ whenever(baseL10n.selectedLanguage).thenReturn(BaseL10n.LANGUAGE.ENGLISH)
+ assertThat(translation.currentLocale, equalTo(Locale.ENGLISH))
+ }
+
+}
--- /dev/null
+package net.pterodactylus.sone.freenet
+
+import freenet.keys.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+
+/**
+ * Unit test for [Key].
+ */
+class FreenetURIsTest {
+
+ private val uri = FreenetURI("SSK@$routingKey,$cryptoKey,$extra/some-site-12/foo/bar.html")
+
+ @Test
+ fun routingKeyIsExtractCorrectly() {
+ assertThat(uri.routingKeyString, equalTo(routingKey))
+ }
+
+}
+
+private const val routingKey = "NfUYvxDwU9vqb2mh-qdT~DYJ6U0XNbxMGGoLe0aCHJs"
+private const val cryptoKey = "Miglsgix0VR56ZiPl4NgjnUd~UdrnHqIvXJ3KKHmxmI"
+private const val extra = "AQACAAE"
package net.pterodactylus.sone.freenet
-import freenet.l10n.BaseL10n
-import freenet.l10n.BaseL10n.LANGUAGE.ENGLISH
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import net.pterodactylus.util.template.TemplateContext
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Before
-import org.junit.Test
-import org.mockito.ArgumentMatchers.anyString
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import java.util.*
+import kotlin.collections.*
/**
* Unit test for [L10nFilter].
*/
class L10nFilterTest {
- private val l10n = mock<BaseL10n>()
- private val filter = L10nFilter(l10n)
- private val templateContext = mock<TemplateContext>()
private val translations = mutableMapOf<String, String>()
-
- @Before
- fun setupL10n() {
- whenever(l10n.selectedLanguage).thenReturn(ENGLISH)
- whenever(l10n.getString(anyString())).then { translations[it.arguments[0]] }
+ private val translation = object : Translation {
+ override val currentLocale = Locale.ENGLISH
+ override fun translate(key: String): String = translations[key] ?: ""
}
+ private val filter = L10nFilter(translation)
@Test
fun `translation without parameters returns translated string`() {
translations["data"] = "translated data"
- assertThat(filter.format(templateContext, "data", emptyMap()), equalTo("translated data"))
+ assertThat(filter.format(null, "data", emptyMap()), equalTo("translated data"))
}
@Test
fun `translation with parameters returned translated string`() {
translations["data"] = "translated {0,number} {1}"
- assertThat(filter.format(templateContext, "data", mapOf("0" to 4.5, "1" to "data")), equalTo("translated 4.5 data"))
+ assertThat(filter.format(null, "data", mapOf("0" to 4.5, "1" to "data")), equalTo("translated 4.5 data"))
}
@Test
fun `filter processes l10n text without parameters correctly`() {
translations["data"] = "translated data"
- assertThat(filter.format(templateContext, L10nText("data"), emptyMap()), equalTo("translated data"))
+ assertThat(filter.format(null, L10nText("data"), emptyMap()), equalTo("translated data"))
}
@Test
fun `filter processes l10n text with parameters correctly`() {
translations["data"] = "translated {0,number} {1}"
- assertThat(filter.format(templateContext, L10nText("data", listOf(4.5, "data")), emptyMap()), equalTo("translated 4.5 data"))
+ assertThat(filter.format(null, L10nText("data", listOf(4.5, "data")), emptyMap()), equalTo("translated 4.5 data"))
}
@Test
fun `filter does not replace values if there are no parameters`() {
translations["data"] = "{link}"
- assertThat(filter.format(templateContext, "data", emptyMap()), equalTo("{link}"))
+ assertThat(filter.format(null, "data", emptyMap()), equalTo("{link}"))
}
}
--- /dev/null
+/**
+ * Sone - FredPluginConnectorTest.kt - Copyright © 2019 David ‘Bombe’ Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* Fred-based plugin stuff is mostly deprecated. ¯\_(ツ)_/¯ */
+@file:Suppress("DEPRECATION")
+
+package net.pterodactylus.sone.freenet.plugin
+
+import freenet.pluginmanager.*
+import freenet.support.*
+import freenet.support.api.*
+import freenet.support.io.*
+import kotlinx.coroutines.*
+import net.pterodactylus.sone.freenet.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import org.junit.rules.*
+import kotlin.concurrent.*
+
+class FredPluginConnectorTest {
+
+ @Rule
+ @JvmField
+ val expectedException = ExpectedException.none()!!
+
+ @Test
+ fun `connector throws exception if plugin can not be found`() = runBlocking {
+ val pluginConnector = FredPluginConnector(pluginRespiratorFacade)
+ expectedException.expect(PluginException::class.java)
+ pluginConnector.sendRequest("wrong.plugin", requestFields, requestData)
+ Unit
+ }
+
+ @Test
+ fun `connector returns correct fields and data`() = runBlocking {
+ val pluginConnector = FredPluginConnector(pluginRespiratorFacade)
+ val reply = pluginConnector.sendRequest("test.plugin", requestFields, requestData)
+ assertThat(reply.fields, equalTo(responseFields))
+ assertThat(reply.data, equalTo(responseData))
+ }
+
+}
+
+private val requestFields = SimpleFieldSetBuilder().put("foo", "bar").get()
+private val requestData: Bucket? = ArrayBucket(byteArrayOf(1, 2))
+private val responseFields = SimpleFieldSetBuilder().put("baz", "quo").get()
+private val responseData: Bucket? = ArrayBucket(byteArrayOf(3, 4))
+
+private val pluginRespiratorFacade = object : PluginRespiratorFacade {
+ override fun getPluginTalker(pluginTalker: FredPluginTalker, pluginName: String, identifier: String) =
+ if (pluginName == "test.plugin") {
+ object : PluginTalkerFacade {
+ override fun send(pluginParameters: SimpleFieldSet, data: Bucket?) {
+ if ((pluginParameters == requestFields) && (data == requestData)) {
+ thread { pluginTalker.onReply(pluginName, identifier, responseFields, responseData) }
+ }
+ }
+ }
+ } else {
+ throw PluginNotFoundException()
+ }
+}
--- /dev/null
+package net.pterodactylus.sone.freenet.plugin
+
+import freenet.pluginmanager.*
+import freenet.support.*
+import freenet.support.api.*
+import freenet.support.io.*
+import net.pterodactylus.sone.freenet.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.mockito.ArgumentMatchers.*
+import kotlin.test.*
+
+/**
+ * Unit test for [FredPluginRespiratorFacade] and [FredPluginTalkerFacade].
+ */
+@Suppress("DEPRECATION")
+class PluginRespiratorFacadeTest {
+
+ @Test
+ fun `respirator facade creates correct plugin talker facade`() {
+ val pluginTalkerSendParameters = mutableListOf<PluginTalkerSendParameters>()
+ val originalPluginTalker = mock<PluginTalker>().apply {
+ whenever(send(any(), any())).then { invocation ->
+ pluginTalkerSendParameters += PluginTalkerSendParameters(invocation.getArgument(0), invocation.getArgument(1))
+ Unit
+ }
+ }
+ val fredPluginTalker = FredPluginTalker { _, _, _, _ -> }
+ val pluginRespirator = mock<PluginRespirator>().apply {
+ whenever(getPluginTalker(fredPluginTalker, "test.plugin", "test-request-1")).thenReturn(originalPluginTalker)
+ }
+ val pluginRespiratorFacade = FredPluginRespiratorFacade(pluginRespirator)
+ val pluginTalker = pluginRespiratorFacade.getPluginTalker(fredPluginTalker, "test.plugin", "test-request-1")
+ pluginTalker.send(fields, data)
+ assertThat(pluginTalkerSendParameters, hasSize(1))
+ assertThat(pluginTalkerSendParameters[0].parameter, equalTo(fields))
+ assertThat(pluginTalkerSendParameters[0].data, equalTo(data))
+ }
+
+}
+
+private val fields = SimpleFieldSetBuilder().put("foo", "bar").get()
+private val data: Bucket? = ArrayBucket(byteArrayOf(1, 2))
+
+private data class PluginTalkerSendParameters(val parameter: SimpleFieldSet, val data: Bucket?)
--- /dev/null
+/*
+ * Sone - DefaultIdentityTest.java - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+import com.google.common.collect.ImmutableMap.*
+import net.pterodactylus.sone.test.*
+import net.pterodactylus.sone.test.Matchers.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.containsInAnyOrder
+import org.hamcrest.Matchers.empty
+import org.hamcrest.Matchers.equalTo
+import org.hamcrest.Matchers.hasEntry
+import org.hamcrest.Matchers.not
+import org.hamcrest.Matchers.nullValue
+import org.hamcrest.collection.IsIterableContainingInOrder.contains
+import org.junit.*
+
+/**
+ * Unit test for [DefaultIdentity].
+ */
+open class DefaultIdentityTest {
+
+ protected open val identity = DefaultIdentity("Id", "Nickname", "RequestURI")
+
+ @Test
+ fun `identity can be created`() {
+ assertThat(identity.id, equalTo("Id"))
+ assertThat(identity.nickname, equalTo("Nickname"))
+ assertThat(identity.requestUri, equalTo("RequestURI"))
+ assertThat(identity.contexts, empty())
+ assertThat(identity.properties, equalTo(emptyMap()))
+ }
+
+ @Test
+ fun `contexts are added correctly`() {
+ identity.addContext("Test")
+ assertThat(identity.contexts, contains("Test"))
+ assertThat(identity.hasContext("Test"), equalTo(true))
+ }
+
+ @Test
+ fun `contexts are removed correctly`() {
+ identity.addContext("Test")
+ identity.removeContext("Test")
+ assertThat(identity.contexts, empty())
+ assertThat(identity.hasContext("Test"), equalTo(false))
+ }
+
+ @Test
+ fun `contexts are set correctly in bulk`() {
+ identity.addContext("Test")
+ identity.contexts = setOf("Test1", "Test2")
+ assertThat(identity.contexts, containsInAnyOrder("Test1", "Test2"))
+ assertThat(identity.hasContext("Test"), equalTo(false))
+ assertThat(identity.hasContext("Test1"), equalTo(true))
+ assertThat(identity.hasContext("Test2"), equalTo(true))
+ }
+
+ @Test
+ fun `properties are added correctly`() {
+ identity.setProperty("Key", "Value")
+ assertThat(identity.properties.size, equalTo(1))
+ assertThat(identity.properties, hasEntry("Key", "Value"))
+ assertThat(identity.getProperty("Key"), equalTo("Value"))
+ }
+
+ @Test
+ fun `properties are removed correctly`() {
+ identity.setProperty("Key", "Value")
+ identity.removeProperty("Key")
+ assertThat(identity.properties, equalTo(emptyMap()))
+ assertThat(identity.getProperty("Key"), nullValue())
+ }
+
+ @Test
+ fun `properties are set correctly in bulk`() {
+ identity.setProperty("Key", "Value")
+ identity.properties = of("Key1", "Value1", "Key2", "Value2")
+ assertThat(identity.properties.size, equalTo(2))
+ assertThat(identity.getProperty("Key"), nullValue())
+ assertThat(identity.getProperty("Key1"), equalTo("Value1"))
+ assertThat(identity.getProperty("Key2"), equalTo("Value2"))
+ }
+
+ @Test
+ fun `trust relationships are added correctly`() {
+ val ownIdentity = mock<OwnIdentity>()
+ val trust = mock<Trust>()
+ identity.setTrust(ownIdentity, trust)
+ assertThat(identity.getTrust(ownIdentity), equalTo(trust))
+ }
+
+ @Test
+ fun `trust relationships are removed correctly`() {
+ val ownIdentity = mock<OwnIdentity>()
+ val trust = mock<Trust>()
+ identity.setTrust(ownIdentity, trust)
+ identity.removeTrust(ownIdentity)
+ assertThat(identity.getTrust(ownIdentity), nullValue())
+ }
+
+ @Test
+ fun `identities with the same id are equal`() {
+ val identity2 = DefaultIdentity("Id", "Nickname2", "RequestURI2")
+ assertThat(identity2, equalTo(identity))
+ assertThat(identity, equalTo(identity2))
+ }
+
+ @Test
+ fun `two equal identities have the same hash code`() {
+ val identity2 = DefaultIdentity("Id", "Nickname2", "RequestURI2")
+ assertThat(identity.hashCode(), equalTo(identity2.hashCode()))
+ }
+
+ @Test
+ fun `null does not match an identity`() {
+ assertThat(identity, not(equalTo<Any>(null as Any?)))
+ }
+
+ @Test
+ fun `toString() contains id and nickname`() {
+ val identityString = identity.toString()
+ assertThat(identityString, matchesRegex(".*\\bId\\b.*"))
+ assertThat(identityString, matchesRegex(".*\\bNickname\\b.*"))
+ }
+
+}
--- /dev/null
+/*
+ * Sone - DefaultOwnIdentityTest.java - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+
+/**
+ * Unit test for [DefaultOwnIdentity].
+ */
+class DefaultOwnIdentityTest : DefaultIdentityTest() {
+
+ override val identity = DefaultOwnIdentity("Id", "Nickname", "RequestURI", "InsertURI")
+
+ @Test
+ fun `own identity can be created`() {
+ assertThat((identity as OwnIdentity).insertUri, equalTo("InsertURI"))
+ }
+
+}
--- /dev/null
+/*
+ * Sone - Identities.java - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+fun createOwnIdentity(id: String, contexts: Set<String>, vararg properties: Pair<String, String>): OwnIdentity {
+ val ownIdentity = DefaultOwnIdentity(id, "Nickname$id", "Request$id", "Insert$id")
+ setContextsAndPropertiesOnIdentity(ownIdentity, contexts, mapOf(*properties))
+ return ownIdentity
+}
+
+fun createIdentity(id: String, contexts: Set<String>, vararg properties: Pair<String, String>): Identity {
+ val identity = DefaultIdentity(id, "Nickname$id", "Request$id")
+ setContextsAndPropertiesOnIdentity(identity, contexts, mapOf(*properties))
+ return identity
+}
+
+private fun setContextsAndPropertiesOnIdentity(identity: Identity, contexts: Set<String>, properties: Map<String, String>) {
+ identity.contexts = contexts
+ identity.properties = properties
+}
--- /dev/null
+/*
+ * Sone - IdentityChangeDetectorTest.java - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+
+/**
+ * Unit test for [IdentityChangeDetector].
+ */
+class IdentityChangeDetectorTest {
+
+ private val identityChangeDetector = IdentityChangeDetector(createOldIdentities())
+ private val newIdentities = mutableListOf<Identity>()
+ private val removedIdentities = mutableListOf<Identity>()
+ private val changedIdentities = mutableListOf<Identity>()
+ private val unchangedIdentities = mutableListOf<Identity>()
+
+ @Before
+ fun setup() {
+ identityChangeDetector.onNewIdentity = { identity -> newIdentities.add(identity) }
+ identityChangeDetector.onRemovedIdentity = { identity -> removedIdentities.add(identity) }
+ identityChangeDetector.onChangedIdentity = { identity -> changedIdentities.add(identity) }
+ identityChangeDetector.onUnchangedIdentity = { identity -> unchangedIdentities.add(identity) }
+ }
+
+ @Test
+ fun `no differences are detected when sending the old identities again`() {
+ identityChangeDetector.detectChanges(createOldIdentities())
+ assertThat(newIdentities, empty())
+ assertThat(removedIdentities, empty())
+ assertThat(changedIdentities, empty())
+ assertThat(unchangedIdentities, containsInAnyOrder(createIdentity1(), createIdentity2(), createIdentity3()))
+ }
+
+ @Test
+ fun `detect that an identity was removed`() {
+ identityChangeDetector.detectChanges(listOf(createIdentity1(), createIdentity3()))
+ assertThat(newIdentities, empty())
+ assertThat(removedIdentities, containsInAnyOrder(createIdentity2()))
+ assertThat(changedIdentities, empty())
+ assertThat(unchangedIdentities, containsInAnyOrder(createIdentity1(), createIdentity3()))
+ }
+
+ @Test
+ fun `detect that an identity was added`() {
+ identityChangeDetector.detectChanges(listOf(createIdentity1(), createIdentity2(), createIdentity3(), createIdentity4()))
+ assertThat(newIdentities, containsInAnyOrder(createIdentity4()))
+ assertThat(removedIdentities, empty())
+ assertThat(changedIdentities, empty())
+ assertThat(unchangedIdentities, containsInAnyOrder(createIdentity1(), createIdentity2(), createIdentity3()))
+ }
+
+ @Test
+ fun `detect that a context was removed`() {
+ val identity2 = createIdentity2()
+ identity2.removeContext("Context C")
+ identityChangeDetector.detectChanges(listOf(createIdentity1(), identity2, createIdentity3()))
+ assertThat(newIdentities, empty())
+ assertThat(removedIdentities, empty())
+ assertThat(changedIdentities, containsInAnyOrder(identity2))
+ assertThat(unchangedIdentities, containsInAnyOrder(createIdentity1(), createIdentity3()))
+ }
+
+ @Test
+ fun `detect that a context was added`() {
+ val identity2 = createIdentity2()
+ identity2.addContext("Context C1")
+ identityChangeDetector.detectChanges(listOf(createIdentity1(), identity2, createIdentity3()))
+ assertThat(newIdentities, empty())
+ assertThat(removedIdentities, empty())
+ assertThat(changedIdentities, containsInAnyOrder(identity2))
+ assertThat(unchangedIdentities, containsInAnyOrder(createIdentity1(), createIdentity3()))
+ }
+
+ @Test
+ fun `detect that a property was removed`() {
+ val identity1 = createIdentity1()
+ identity1.removeProperty("Key A")
+ identityChangeDetector.detectChanges(listOf(identity1, createIdentity2(), createIdentity3()))
+ assertThat(newIdentities, empty())
+ assertThat(removedIdentities, empty())
+ assertThat(changedIdentities, containsInAnyOrder(identity1))
+ assertThat(unchangedIdentities, containsInAnyOrder(createIdentity2(), createIdentity3()))
+ }
+
+ @Test
+ fun `detect that a property was added`() {
+ val identity3 = createIdentity3()
+ identity3.setProperty("Key A", "Value A")
+ identityChangeDetector.detectChanges(listOf(createIdentity1(), createIdentity2(), identity3))
+ assertThat(newIdentities, empty())
+ assertThat(removedIdentities, empty())
+ assertThat(changedIdentities, containsInAnyOrder(identity3))
+ assertThat(unchangedIdentities, containsInAnyOrder(createIdentity1(), createIdentity2()))
+ }
+
+ @Test
+ fun `detect that a property was changed`() {
+ val identity3 = createIdentity3()
+ identity3.setProperty("Key E", "Value F")
+ identityChangeDetector.detectChanges(listOf(createIdentity1(), createIdentity2(), identity3))
+ assertThat(newIdentities, empty())
+ assertThat(removedIdentities, empty())
+ assertThat(changedIdentities, containsInAnyOrder(identity3))
+ assertThat(unchangedIdentities, containsInAnyOrder(createIdentity1(), createIdentity2()))
+ }
+
+ @Test
+ fun `no removed identities are detected without an identity processor`() {
+ identityChangeDetector.onRemovedIdentity = null
+ identityChangeDetector.detectChanges(listOf(createIdentity1(), createIdentity3()))
+ assertThat(removedIdentities, empty())
+ }
+
+ @Test
+ fun `no added identities are detected without an identity processor`() {
+ identityChangeDetector.onNewIdentity = null
+ identityChangeDetector.detectChanges(listOf(createIdentity1(), createIdentity2(), createIdentity3(), createIdentity4()))
+ assertThat(newIdentities, empty())
+ }
+
+ private fun createOldIdentities() =
+ listOf(createIdentity1(), createIdentity2(), createIdentity3())
+
+ private fun createIdentity1() =
+ createIdentity("Test1", setOf("Context A", "Context B"), "Key A" to "Value A", "Key B" to "Value B")
+
+ private fun createIdentity2() =
+ createIdentity("Test2", setOf("Context C", "Context D"), "Key C" to "Value C", "Key D" to "Value D")
+
+ private fun createIdentity3() =
+ createIdentity("Test3", setOf("Context E", "Context F"), "Key E" to "Value E", "Key F" to "Value F")
+
+ private fun createIdentity4() =
+ createIdentity("Test4", setOf("Context G", "Context H"), "Key G" to "Value G", "Key H" to "Value H")
+
+}
--- /dev/null
+/*
+ * Sone - IdentityChangeEventSenderTest.java - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+import com.google.common.eventbus.*
+import net.pterodactylus.sone.freenet.wot.event.*
+import net.pterodactylus.sone.test.*
+import org.junit.*
+import org.mockito.ArgumentMatchers.eq
+import org.mockito.Mockito.verify
+
+/**
+ * Unit test for [IdentityChangeEventSender].
+ */
+class IdentityChangeEventSenderTest {
+
+ private val eventBus = mock<EventBus>()
+ private val ownIdentities = listOf(
+ createOwnIdentity("O1", setOf("Test"), "KeyA" to "ValueA"),
+ createOwnIdentity("O2", setOf("Test2"), "KeyB" to "ValueB"),
+ createOwnIdentity("O3", setOf("Test3"), "KeyC" to "ValueC")
+ )
+ private val identities = listOf(
+ createIdentity("I1", setOf()),
+ createIdentity("I2", setOf()),
+ createIdentity("I3", setOf()),
+ createIdentity("I2", setOf("Test"))
+ )
+ private val identityChangeEventSender = IdentityChangeEventSender(eventBus, createOldIdentities())
+
+ @Test
+ fun addingAnOwnIdentityIsDetectedAndReportedCorrectly() {
+ val newIdentities = createNewIdentities()
+ identityChangeEventSender.detectChanges(newIdentities)
+ verify(eventBus).post(eq(OwnIdentityRemovedEvent(ownIdentities[0])))
+ verify(eventBus).post(eq(IdentityRemovedEvent(ownIdentities[0], identities[0])))
+ verify(eventBus).post(eq(IdentityRemovedEvent(ownIdentities[0], identities[1])))
+ verify(eventBus).post(eq(OwnIdentityAddedEvent(ownIdentities[2])))
+ verify(eventBus).post(eq(IdentityAddedEvent(ownIdentities[2], identities[1])))
+ verify(eventBus).post(eq(IdentityAddedEvent(ownIdentities[2], identities[2])))
+ verify(eventBus).post(eq(IdentityRemovedEvent(ownIdentities[1], identities[0])))
+ verify(eventBus).post(eq(IdentityAddedEvent(ownIdentities[1], identities[2])))
+ verify(eventBus).post(eq(IdentityUpdatedEvent(ownIdentities[1], identities[1])))
+ }
+
+ private fun createNewIdentities() = mapOf(
+ ownIdentities[1] to listOf(identities[3], identities[2]),
+ ownIdentities[2] to listOf(identities[1], identities[2])
+ )
+
+ private fun createOldIdentities() = mapOf(
+ ownIdentities[0] to listOf(identities[0], identities[1]),
+ ownIdentities[1] to listOf(identities[0], identities[1])
+ )
+
+}
--- /dev/null
+/*
+ * Sone - IdentityLoaderTest.java - Copyright © 2013–2019 David Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.freenet.wot
+
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+
+/**
+ * Unit test for [IdentityLoader].
+ */
+class IdentityLoaderTest {
+
+ private val ownIdentities = createOwnIdentities()
+ private val webOfTrustConnector = object : TestWebOfTrustConnector() {
+ override fun loadAllOwnIdentities() = ownIdentities.toSet()
+ override fun loadTrustedIdentities(ownIdentity: OwnIdentity, context: String?) =
+ when (ownIdentity) {
+ ownIdentities[0] -> createTrustedIdentitiesForFirstOwnIdentity()
+ ownIdentities[1] -> createTrustedIdentitiesForSecondOwnIdentity()
+ ownIdentities[2] -> createTrustedIdentitiesForThirdOwnIdentity()
+ ownIdentities[3] -> createTrustedIdentitiesForFourthOwnIdentity()
+ else -> throw RuntimeException()
+ }
+ }
+
+ @Test
+ fun loadingIdentities() {
+ val identityLoader = IdentityLoader(webOfTrustConnector, Context("Test"))
+ val identities = identityLoader.loadIdentities()
+ assertThat(identities.keys, hasSize(4))
+ assertThat(identities.keys, containsInAnyOrder(ownIdentities[0], ownIdentities[1], ownIdentities[2], ownIdentities[3]))
+ verifyIdentitiesForOwnIdentity(identities, ownIdentities[0], createTrustedIdentitiesForFirstOwnIdentity())
+ verifyIdentitiesForOwnIdentity(identities, ownIdentities[1], createTrustedIdentitiesForSecondOwnIdentity())
+ verifyIdentitiesForOwnIdentity(identities, ownIdentities[2], emptySet())
+ verifyIdentitiesForOwnIdentity(identities, ownIdentities[3], createTrustedIdentitiesForFourthOwnIdentity())
+ }
+
+ @Test
+ fun loadingIdentitiesWithoutContext() {
+ val identityLoaderWithoutContext = IdentityLoader(webOfTrustConnector)
+ val identities = identityLoaderWithoutContext.loadIdentities()
+ assertThat(identities.keys, hasSize(4))
+ assertThat(identities.keys, containsInAnyOrder(ownIdentities[0], ownIdentities[1], ownIdentities[2], ownIdentities[3]))
+ verifyIdentitiesForOwnIdentity(identities, ownIdentities[0], createTrustedIdentitiesForFirstOwnIdentity())
+ verifyIdentitiesForOwnIdentity(identities, ownIdentities[1], createTrustedIdentitiesForSecondOwnIdentity())
+ verifyIdentitiesForOwnIdentity(identities, ownIdentities[2], createTrustedIdentitiesForThirdOwnIdentity())
+ verifyIdentitiesForOwnIdentity(identities, ownIdentities[3], createTrustedIdentitiesForFourthOwnIdentity())
+ }
+
+ private fun verifyIdentitiesForOwnIdentity(identities: Map<OwnIdentity, Collection<Identity>>, ownIdentity: OwnIdentity, trustedIdentities: Set<Identity>) {
+ assertThat(identities[ownIdentity], equalTo<Collection<Identity>>(trustedIdentities))
+ }
+
+}
+
+private fun createOwnIdentities() = listOf(
+ createOwnIdentity("O1", "ON1", "OR1", "OI1", setOf("Test", "Test2"), mapOf("KeyA" to "ValueA", "KeyB" to "ValueB")),
+ createOwnIdentity("O2", "ON2", "OR2", "OI2", setOf("Test"), mapOf("KeyC" to "ValueC")),
+ createOwnIdentity("O3", "ON3", "OR3", "OI3", setOf("Test2"), mapOf("KeyE" to "ValueE", "KeyD" to "ValueD")),
+ createOwnIdentity("O4", "ON4", "OR$", "OI4", setOf("Test"), mapOf("KeyA" to "ValueA", "KeyD" to "ValueD"))
+)
+
+private fun createTrustedIdentitiesForFirstOwnIdentity() = setOf(
+ createIdentity("I11", "IN11", "IR11", setOf("Test"), mapOf("KeyA" to "ValueA"))
+)
+
+private fun createTrustedIdentitiesForSecondOwnIdentity() = setOf(
+ createIdentity("I21", "IN21", "IR21", setOf("Test", "Test2"), mapOf("KeyB" to "ValueB"))
+)
+
+private fun createTrustedIdentitiesForThirdOwnIdentity() = setOf(
+ createIdentity("I31", "IN31", "IR31", setOf("Test", "Test3"), mapOf("KeyC" to "ValueC"))
+)
+
+private fun createTrustedIdentitiesForFourthOwnIdentity(): Set<Identity> = emptySet()
+
+private fun createOwnIdentity(id: String, nickname: String, requestUri: String, insertUri: String, contexts: Set<String>, properties: Map<String, String>): OwnIdentity =
+ DefaultOwnIdentity(id, nickname, requestUri, insertUri).apply {
+ setContexts(contexts)
+ this.properties = properties
+ }
+
+private fun createIdentity(id: String, nickname: String, requestUri: String, contexts: Set<String>, properties: Map<String, String>): Identity =
+ DefaultIdentity(id, nickname, requestUri).apply {
+ setContexts(contexts)
+ this.properties = properties
+ }
+
+private open class TestWebOfTrustConnector : WebOfTrustConnector {
+
+ override fun loadAllOwnIdentities() = emptySet<OwnIdentity>()
+ override fun loadTrustedIdentities(ownIdentity: OwnIdentity, context: String?) = emptySet<Identity>()
+ override fun addContext(ownIdentity: OwnIdentity, context: String) = Unit
+ override fun removeContext(ownIdentity: OwnIdentity, context: String) = Unit
+ override fun setProperty(ownIdentity: OwnIdentity, name: String, value: String) = Unit
+ override fun removeProperty(ownIdentity: OwnIdentity, name: String) = Unit
+ override fun getTrust(ownIdentity: OwnIdentity, identity: Identity) = Trust(null, null, null)
+ override fun setTrust(ownIdentity: OwnIdentity, identity: Identity, trust: Int, comment: String) = Unit
+ override fun removeTrust(ownIdentity: OwnIdentity, identity: Identity) = Unit
+ override fun ping() = Unit
+
+}
--- /dev/null
+package net.pterodactylus.sone.freenet.wot
+
+import freenet.support.*
+import freenet.support.api.*
+import net.pterodactylus.sone.freenet.*
+import net.pterodactylus.sone.freenet.plugin.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.hamcrest.core.*
+import kotlin.test.*
+
+/**
+ * Unit test for [PluginWebOfTrustConnector].
+ */
+class PluginWebOfTrustConnectorTest {
+
+ private val ownIdentity = DefaultOwnIdentity("id", "nickname", "requestUri", "insertUri")
+ private val identity = DefaultIdentity("id-a", "alpha", "url://alpha")
+
+ @Test
+ fun `wot plugin can be pinged`() {
+ createPluginConnector("Ping")
+ .connect { ping() }
+ }
+
+ @Test
+ fun `own identities are returned correctly`() {
+ val ownIdentities = createPluginConnector("GetOwnIdentities") {
+ put("Identity0", "id-0")
+ put("RequestURI0", "request-uri-0")
+ put("InsertURI0", "insert-uri-0")
+ put("Nickname0", "nickname-0")
+ put("Contexts0.Context0", "id-0-context-0")
+ put("Properties0.Property0.Name", "id-0-property-0-name")
+ put("Properties0.Property0.Value", "id-0-property-0-value")
+ put("Identity1", "id-1")
+ put("RequestURI1", "request-uri-1")
+ put("InsertURI1", "insert-uri-1")
+ put("Nickname1", "nickname-1")
+ put("Contexts1.Context0", "id-1-context-0")
+ put("Properties1.Property0.Name", "id-1-property-0-name")
+ put("Properties1.Property0.Value", "id-1-property-0-value")
+ }.connect { loadAllOwnIdentities() }
+ assertThat(ownIdentities, containsInAnyOrder(
+ isOwnIdentity("id-0", "nickname-0", "request-uri-0", "insert-uri-0", contains("id-0-context-0"), hasEntry("id-0-property-0-name", "id-0-property-0-value")),
+ isOwnIdentity("id-1", "nickname-1", "request-uri-1", "insert-uri-1", contains("id-1-context-0"), hasEntry("id-1-property-0-name", "id-1-property-0-value"))
+ ))
+ }
+
+ @Test
+ fun `trusted identities are requested with correct own identity`() {
+ createPluginConnector("GetIdentitiesByScore", hasField("Truster", equalTo("id")))
+ .connect { loadTrustedIdentities(ownIdentity) }
+ }
+
+ @Test
+ fun `trusted identities are requested with correct selection parameter`() {
+ createPluginConnector("GetIdentitiesByScore", hasField("Selection", equalTo("+")))
+ .connect { loadTrustedIdentities(ownIdentity) }
+ }
+
+ @Test
+ fun `trusted identities are requested with empty context if null context requested`() {
+ createPluginConnector("GetIdentitiesByScore", hasField("Context", equalTo("")))
+ .connect { loadTrustedIdentities(ownIdentity) }
+ }
+
+ @Test
+ fun `trusted identities are requested with context if context requested`() {
+ createPluginConnector("GetIdentitiesByScore", hasField("Context", equalTo("TestContext")))
+ .connect { loadTrustedIdentities(ownIdentity, "TestContext") }
+ }
+
+ @Test
+ fun `trusted identities are requested with trust values`() {
+ createPluginConnector("GetIdentitiesByScore", hasField("WantTrustValues", equalTo("true")))
+ .connect { loadTrustedIdentities(ownIdentity) }
+ }
+
+ @Test
+ fun `empty list of trusted identities is returned correctly`() {
+ val trustedIdentities = createPluginConnector("GetIdentitiesByScore")
+ .connect { loadTrustedIdentities(ownIdentity) }
+ assertThat(trustedIdentities, empty())
+ }
+
+ @Test
+ fun `trusted identities without context, properties, or trust value are returned correctly`() {
+ val trustedIdentities = createPluginConnector("GetIdentitiesByScore") {
+ put("Identity0", "id0")
+ put("Nickname0", "nickname0")
+ put("RequestURI0", "request-uri0")
+ put("Identity1", "id1")
+ put("Nickname1", "nickname1")
+ put("RequestURI1", "request-uri1")
+ }.connect { loadTrustedIdentities(ownIdentity) }
+ assertThat(trustedIdentities, contains(
+ allOf(
+ isIdentity("id0", "nickname0", "request-uri0", empty<String>(), isEmptyMap()),
+ isTrusted(ownIdentity, isTrust(null, null, null))
+ ),
+ allOf(
+ isIdentity("id1", "nickname1", "request-uri1", empty<String>(), isEmptyMap()),
+ isTrusted(ownIdentity, isTrust(null, null, null))
+ )
+ ))
+ }
+
+ @Test
+ fun `trusted identity without nickname is returned correctly`() {
+ val trustedIdentities = createPluginConnector("GetIdentitiesByScore") {
+ put("Identity0", "id0")
+ put("RequestURI0", "request-uri0")
+ }.connect { loadTrustedIdentities(ownIdentity) }
+ assertThat(trustedIdentities, contains(
+ allOf(
+ isIdentity("id0", null, "request-uri0", empty<String>(), isEmptyMap()),
+ isTrusted(ownIdentity, isTrust(null, null, null))
+ )
+ ))
+ }
+
+ @Test
+ fun `trusted identity with contexts is returned correctly`() {
+ val trustedIdentities = createPluginConnector("GetIdentitiesByScore") {
+ put("Identity0", "id0")
+ put("Nickname0", "nickname0")
+ put("RequestURI0", "request-uri0")
+ put("Contexts0.Context0", "Context0")
+ put("Contexts0.Context1", "Context1")
+ }.connect { loadTrustedIdentities(ownIdentity) }
+ assertThat(trustedIdentities, contains(
+ isIdentity("id0", "nickname0", "request-uri0", containsInAnyOrder("Context0", "Context1"), isEmptyMap())
+ ))
+ }
+
+ @Test
+ fun `trusted identity with properties is returned correctly`() {
+ val trustedIdentities = createPluginConnector("GetIdentitiesByScore") {
+ put("Identity0", "id0")
+ put("Nickname0", "nickname0")
+ put("RequestURI0", "request-uri0")
+ put("Properties0.Property0.Name", "foo")
+ put("Properties0.Property0.Value", "bar")
+ put("Properties0.Property1.Name", "baz")
+ put("Properties0.Property1.Value", "quo")
+ }.connect { loadTrustedIdentities(ownIdentity) }
+ assertThat(trustedIdentities, contains(
+ isIdentity("id0", "nickname0", "request-uri0", empty(), allOf(hasEntry("foo", "bar"), hasEntry("baz", "quo")))
+ ))
+ }
+
+ @Test
+ fun `trusted identity with trust value is returned correctly`() {
+ val trustedIdentities = createPluginConnector("GetIdentitiesByScore") {
+ put("Identity0", "id0")
+ put("Nickname0", "nickname0")
+ put("RequestURI0", "request-uri0")
+ put("Trust0", "12")
+ put("Score0", "34")
+ put("Rank0", "56")
+ }.connect { loadTrustedIdentities(ownIdentity) }
+ assertThat(trustedIdentities, contains(
+ allOf(
+ isIdentity("id0", "nickname0", "request-uri0", empty(), isEmptyMap()),
+ isTrusted(ownIdentity, isTrust(12, 34, 56))
+ )
+ ))
+ }
+
+ @Test
+ fun `adding a context sends the correct own identity id`() {
+ createPluginConnector("AddContext", hasField("Identity", equalTo(ownIdentity.id)))
+ .connect { addContext(ownIdentity, "TestContext") }
+ }
+
+ @Test
+ fun `adding a context sends the correct context`() {
+ createPluginConnector("AddContext", hasField("Context", equalTo("TestContext")))
+ .connect { addContext(ownIdentity, "TestContext") }
+ }
+
+ @Test
+ fun `removing a context sends the correct own identity id`() {
+ createPluginConnector("RemoveContext", hasField("Identity", equalTo(ownIdentity.id)))
+ .connect { removeContext(ownIdentity, "TestContext") }
+ }
+
+ @Test
+ fun `removing a context sends the correct context`() {
+ createPluginConnector("RemoveContext", hasField("Context", equalTo("TestContext")))
+ .connect { removeContext(ownIdentity, "TestContext") }
+ }
+
+ @Test
+ fun `setting a property sends the correct identity id`() {
+ createPluginConnector("SetProperty", hasField("Identity", equalTo(ownIdentity.id)))
+ .connect { setProperty(ownIdentity, "TestProperty", "TestValue") }
+ }
+
+ @Test
+ fun `setting a property sends the correct property name`() {
+ createPluginConnector("SetProperty", hasField("Property", equalTo("TestProperty")))
+ .connect { setProperty(ownIdentity, "TestProperty", "TestValue") }
+ }
+
+ @Test
+ fun `setting a property sends the correct property value`() {
+ createPluginConnector("SetProperty", hasField("Value", equalTo("TestValue")))
+ .connect { setProperty(ownIdentity, "TestProperty", "TestValue") }
+ }
+
+ @Test
+ fun `removing a property sends the correct identity id`() {
+ createPluginConnector("RemoveProperty", hasField("Identity", equalTo(ownIdentity.id)))
+ .connect { removeProperty(ownIdentity, "TestProperty") }
+ }
+
+ @Test
+ fun `removing a property sends the correct property name`() {
+ createPluginConnector("RemoveProperty", hasField("Property", equalTo("TestProperty")))
+ .connect { removeProperty(ownIdentity, "TestProperty") }
+ }
+
+ @Test
+ fun `getting trust sends correct own identity id`() {
+ createPluginConnector("GetIdentity", hasField("Truster", equalTo(ownIdentity.id)))
+ .connect { getTrust(ownIdentity, identity) }
+ }
+
+ @Test
+ fun `getting trust sends correct identity id`() {
+ createPluginConnector("GetIdentity", hasField("Identity", equalTo(identity.id)))
+ .connect { getTrust(ownIdentity, identity) }
+ }
+
+ @Test
+ fun `getting trust returns correct trust values`() {
+ val trust = createPluginConnector("GetIdentity", hasField("Identity", equalTo(identity.id))) {
+ put("Trust", "12")
+ put("Score", "34")
+ put("Rank", "56")
+ }.connect { getTrust(ownIdentity, identity) }
+ assertThat(trust, isTrust(12, 34, 56))
+ }
+
+ @Test
+ fun `getting trust reads incorrect numbers for trust as null`() {
+ val trust = createPluginConnector("GetIdentity", hasField("Identity", equalTo(identity.id))) {
+ put("Trust", "incorrect")
+ put("Score", "34")
+ put("Rank", "56")
+ }.connect { getTrust(ownIdentity, identity) }
+ assertThat(trust, isTrust(null, 34, 56))
+ }
+
+ @Test
+ fun `getting trust reads incorrect numbers for score as null`() {
+ val trust = createPluginConnector("GetIdentity", hasField("Identity", equalTo(identity.id))) {
+ put("Trust", "12")
+ put("Score", "incorrect")
+ put("Rank", "56")
+ }.connect { getTrust(ownIdentity, identity) }
+ assertThat(trust, isTrust(12, null, 56))
+ }
+
+ @Test
+ fun `getting trust reads incorrect numbers for rank as null`() {
+ val trust = createPluginConnector("GetIdentity", hasField("Identity", equalTo(identity.id))) {
+ put("Trust", "12")
+ put("Score", "34")
+ put("Rank", "incorrect")
+ }.connect { getTrust(ownIdentity, identity) }
+ assertThat(trust, isTrust(12, 34, null))
+ }
+
+ @Test
+ fun `setting trust sends correct own identity id`() {
+ createPluginConnector("SetTrust", hasField("Truster", equalTo(ownIdentity.id)))
+ .connect { setTrust(ownIdentity, identity, 123, "Test Trust") }
+ }
+
+ @Test
+ fun `setting trust sends correct identity id`() {
+ createPluginConnector("SetTrust", hasField("Trustee", equalTo(identity.id)))
+ .connect { setTrust(ownIdentity, identity, 123, "Test Trust") }
+ }
+
+ @Test
+ fun `setting trust sends correct trust value`() {
+ createPluginConnector("SetTrust", hasField("Value", equalTo("123")))
+ .connect { setTrust(ownIdentity, identity, 123, "Test Trust") }
+ }
+
+ @Test
+ fun `setting trust sends correct comment`() {
+ createPluginConnector("SetTrust", hasField("Comment", equalTo("Test Trust")))
+ .connect { setTrust(ownIdentity, identity, 123, "Test Trust") }
+ }
+
+ @Test
+ fun `removing trust sends correct own identity id`() {
+ createPluginConnector("RemoveTrust", hasField("Truster", equalTo(ownIdentity.id)))
+ .connect { removeTrust(ownIdentity, identity) }
+ }
+
+ @Test
+ fun `removing trust sends correct identity id`() {
+ createPluginConnector("RemoveTrust", hasField("Trustee", equalTo(identity.id)))
+ .connect { removeTrust(ownIdentity, identity) }
+ }
+
+}
+
+private fun <R> PluginConnector.connect(block: PluginWebOfTrustConnector.() -> R) =
+ PluginWebOfTrustConnector(this).let(block)
+
+fun createPluginConnector(message: String, fieldsMatcher: Matcher<SimpleFieldSet> = IsAnything<SimpleFieldSet>(), build: SimpleFieldSetBuilder.() -> Unit = {}) =
+ object : PluginConnector {
+ override suspend fun sendRequest(pluginName: String, fields: SimpleFieldSet, data: Bucket?) =
+ if ((pluginName != wotPluginName) || (fields.get("Message") != message)) {
+ throw PluginException()
+ } else {
+ assertThat(fields, fieldsMatcher)
+ PluginReply(SimpleFieldSetBuilder().apply(build).get(), null)
+ }
+ }
+
+private const val wotPluginName = "plugins.WebOfTrust.WebOfTrust"
import freenet.clients.http.*
import freenet.node.*
import freenet.pluginmanager.*
+import net.pterodactylus.sone.freenet.plugin.*
import net.pterodactylus.sone.test.*
import org.hamcrest.MatcherAssert.*
import org.hamcrest.Matchers.*
import org.junit.*
+import org.junit.rules.*
+import org.mockito.*
import org.mockito.Mockito.*
/**
*/
class FreenetModuleTest {
+ @Rule
+ @JvmField
+ val expectedException = ExpectedException.none()!!
+
private val sessionManager = mock<SessionManager>()
private val pluginRespirator = deepMock<PluginRespirator>().apply {
whenever(getSessionManager("Sone")).thenReturn(sessionManager)
}
@Test
- fun `plugin respirator is returned correctly`() {
- assertThat(injector.getInstance(), sameInstance(pluginRespirator))
- }
-
- @Test
- fun `plugin respirator is returned as singleton`() {
- verifySingletonInstance<PluginRespirator>()
+ fun `plugin respirator is not bound`() {
+ expectedException.expect(Exception::class.java)
+ injector.getInstance<PluginRespirator>()
}
@Test
}
@Test
- fun `page maker is returned as singleten`() {
+ fun `page maker is returned as singleton`() {
verifySingletonInstance<PageMaker>()
}
+ @Test
+ fun `plugin respirator facade is returned correctly`() {
+ val pluginRespiratorFacade = injector.getInstance<PluginRespiratorFacade>()
+ pluginRespiratorFacade.getPluginTalker(mock(), "test.plugin", "test-request-1")
+ verify(pluginRespirator).getPluginTalker(any(), ArgumentMatchers.eq("test.plugin"), ArgumentMatchers.eq("test-request-1"))
+ }
+
+ @Test
+ fun `plugin respirator facade is returned as singleton`() {
+ verifySingletonInstance<PluginRespiratorFacade>()
+ }
+
+ @Test
+ fun `plugin connector is returned correctly`() {
+ assertThat(injector.getInstance<PluginConnector>(), notNullValue())
+ }
+
+ @Test
+ fun `plugin connector facade is returned as singleton`() {
+ verifySingletonInstance<PluginConnector>()
+ }
+
}
import net.pterodactylus.sone.core.*
import net.pterodactylus.sone.database.*
import net.pterodactylus.sone.database.memory.*
+import net.pterodactylus.sone.freenet.*
+import net.pterodactylus.sone.freenet.plugin.*
import net.pterodactylus.sone.freenet.wot.*
import net.pterodactylus.sone.test.*
import net.pterodactylus.util.config.*
createInjector(
SoneModule(sonePlugin, EventBus()),
FreenetInterface::class.isProvidedByDeepMock(),
- PluginRespirator::class.isProvidedByDeepMock()
+ PluginRespiratorFacade::class.isProvidedByDeepMock(),
+ PluginConnector::class.isProvidedByDeepMock()
)
}
}
@Test
- fun `base l10n is bound correctly`() {
- assertThat(injector.getInstance(), sameInstance(l10n.base))
+ fun `translation is bound correctly`() {
+ assertThat(injector.getInstance<Translation>(), notNullValue())
}
@Test
val injector = createInjector(
SoneModule(sonePlugin, eventBus),
FreenetInterface::class.isProvidedByDeepMock(),
- PluginRespirator::class.isProvidedByDeepMock()
+ PluginRespiratorFacade::class.isProvidedByDeepMock(),
+ PluginConnector::class.isProvidedByDeepMock()
)
val core = injector.getInstance<Core>()
verify(eventBus).register(core)
assertThat(firstMetricRegistry, sameInstance(secondMetricRegistry))
}
+ @Test
+ fun `wot connector can be created`() {
+ assertThat(injector.getInstance<WebOfTrustConnector>(), notNullValue())
+ }
+
+ @Test
+ fun `wot connector is created as singleton`() {
+ val firstWebOfTrustConnector = injector.getInstance<WebOfTrustConnector>()
+ val secondWebOfTrustConnector = injector.getInstance<WebOfTrustConnector>()
+ assertThat(firstWebOfTrustConnector, sameInstance(secondWebOfTrustConnector))
+ }
+
}
--- /dev/null
+package net.pterodactylus.sone.notify
+
+import net.pterodactylus.util.notify.*
+import net.pterodactylus.util.template.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import java.util.concurrent.atomic.*
+
+/**
+ * Unit test for [ListNotification].
+ */
+class ListNotificationTest {
+
+ private val template = Template()
+ private val listNotification = ListNotification<String>(ID, KEY, template)
+
+ @Test
+ fun `creating a list notification sets empty iterable on element key in template context`() {
+ assertThat(template.initialContext.get(KEY) as Iterable<*>, emptyIterable())
+ }
+
+ @Test
+ @Suppress("UNCHECKED_CAST")
+ fun `list in template context gets updated when elements are added`() {
+ listNotification.add("a")
+ listNotification.add("b")
+ assertThat(template.initialContext.get(KEY) as Iterable<String>, contains("a", "b"))
+ }
+
+ @Test
+ fun `new list notification has no element`() {
+ assertThat(listNotification.elements, emptyIterable())
+ }
+
+ @Test
+ fun `new list notification is empty`() {
+ assertThat(listNotification.isEmpty, equalTo(true))
+ }
+
+ @Test
+ fun `list notification retains set elements`() {
+ listNotification.setElements(listOf("a", "b", "c"))
+ assertThat(listNotification.elements, contains("a", "b", "c"))
+ }
+
+ @Test
+ fun `list notification deduplicates set elements`() {
+ listNotification.setElements(listOf("a", "b", "a"))
+ assertThat(listNotification.elements, contains("a", "b"))
+ }
+
+ @Test
+ fun `list notification retains added elements`() {
+ listNotification.add("a")
+ listNotification.add("b")
+ listNotification.add("c")
+ assertThat(listNotification.elements, contains("a", "b", "c"))
+ }
+
+ @Test
+ fun `list notification deduplicates elements`() {
+ listNotification.add("a")
+ listNotification.add("b")
+ listNotification.add("a")
+ assertThat(listNotification.elements, contains("a", "b"))
+ }
+
+ @Test
+ fun `list notification removes correct element`() {
+ listNotification.setElements(listOf("a", "b", "c"))
+ listNotification.remove("b")
+ assertThat(listNotification.elements, contains("a", "c"))
+ }
+
+ @Test
+ fun `removing the last element dismisses the notification`() {
+ val notificationDismissed = AtomicBoolean()
+ val notificationListener = NotificationListener { notificationDismissed.set(it == listNotification) }
+ listNotification.addNotificationListener(notificationListener)
+ listNotification.add("a")
+ listNotification.remove("a")
+ assertThat(notificationDismissed.get(), equalTo(true))
+ }
+
+ @Test
+ fun `dismissing the list notification removes all elements`() {
+ listNotification.setElements(listOf("a", "b", "c"))
+ listNotification.dismiss()
+ assertThat(listNotification.elements, emptyIterable())
+ }
+
+ @Test
+ fun `list notification with different elements is not equal`() {
+ val secondNotification = ListNotification<String>(ID, KEY, template)
+ listNotification.add("a")
+ secondNotification.add("b")
+ assertThat(listNotification, not(equalTo(secondNotification)))
+ }
+
+ @Test
+ fun `list notification with different key is not equal`() {
+ val secondNotification = ListNotification<String>(ID, OTHER_KEY, template)
+ assertThat(listNotification, not(equalTo(secondNotification)))
+ }
+
+ @Test
+ fun `copied notifications have the same hash code`() {
+ val secondNotification = ListNotification(listNotification)
+ listNotification.add("a")
+ secondNotification.add("a")
+ listNotification.setLastUpdateTime(secondNotification.lastUpdatedTime)
+ assertThat(listNotification.hashCode(), equalTo(secondNotification.hashCode()))
+ }
+
+ @Test
+ fun `list notification is not equal to other objects`() {
+ assertThat(listNotification, not(equalTo(Any())))
+ }
+
+}
+
+private const val ID = "notification-id"
+private const val KEY = "element-key"
+private const val OTHER_KEY = "other-key"
--- /dev/null
+/**
+ * Sone - HistogramRendererTest.kt - Copyright © 2019 David ‘Bombe’ Roden
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+package net.pterodactylus.sone.template
+
+import com.codahale.metrics.*
+import net.pterodactylus.sone.freenet.*
+import net.pterodactylus.util.template.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.jsoup.*
+import org.jsoup.nodes.*
+import org.junit.*
+import java.util.*
+
+/**
+ * Unit test for [HistogramRenderer].
+ */
+class HistogramRendererTest {
+
+ private val translation = object : Translation {
+ override val currentLocale = Locale.ENGLISH
+ override fun translate(key: String) = "Metric Name".takeIf { key == "Page.Metrics.TestHistogram.Title" } ?: ""
+ }
+ private val metricRenderer = HistogramRenderer()
+ private val templateContext = TemplateContext().apply {
+ addFilter("html", HtmlFilter())
+ addFilter("duration", DurationFormatFilter())
+ addFilter("l10n", L10nFilter(translation))
+ }
+
+ @Test
+ fun `histogram is rendered as table row`() {
+ createAndVerifyTableRow {
+ assertThat(it.nodeName(), equalTo("tr"))
+ }
+ }
+
+ @Test
+ fun `histogram has eleven columns`() {
+ createAndVerifyTableRow {
+ assertThat(it.getElementsByTag("td"), hasSize(11))
+ }
+ }
+
+ @Test
+ fun `first column contains translated metric name`() {
+ createAndVerifyTableRow(mapOf("name" to "test.histogram")) {
+ assertThat(it.getElementsByTag("td")[0].text(), equalTo("Metric Name"))
+ }
+ }
+
+ @Test
+ fun `second column is numeric`() {
+ verifyColumnIsNumeric(1)
+ }
+
+ @Test
+ fun `second column contains count`() {
+ createAndVerifyTableRow {
+ assertThat(it.getElementsByTag("td")[1].text(), equalTo("2001"))
+ }
+ }
+
+ @Test
+ fun `third column is numeric`() {
+ verifyColumnIsNumeric(2)
+ }
+
+ @Test
+ fun `third column contains min value`() {
+ createAndVerifyTableRow {
+ assertThat(it.getElementsByTag("td")[2].text(), equalTo("2.0ms"))
+ }
+ }
+
+ @Test
+ fun `fourth column is numeric`() {
+ verifyColumnIsNumeric(3)
+ }
+
+ @Test
+ fun `fourth column contains max value`() {
+ createAndVerifyTableRow {
+ assertThat(it.getElementsByTag("td")[3].text(), equalTo("998.0ms"))
+ }
+ }
+
+ @Test
+ fun `fifth column is numeric`() {
+ verifyColumnIsNumeric(4)
+ }
+
+ @Test
+ fun `fifth column contains mean value`() {
+ createAndVerifyTableRow {
+ assertThat(it.getElementsByTag("td")[4].text(), equalTo("492.7ms"))
+ }
+ }
+
+ @Test
+ fun `sixth column is numeric`() {
+ verifyColumnIsNumeric(5)
+ }
+
+ @Test
+ fun `sixth column contains median value`() {
+ createAndVerifyTableRow {
+ assertThat(it.getElementsByTag("td")[5].text(), equalTo("483.6ms"))
+ }
+ }
+
+ @Test
+ fun `seventh column is numeric`() {
+ verifyColumnIsNumeric(6)
+ }
+
+ @Test
+ fun `seventh column contains 75th percentile`() {
+ createAndVerifyTableRow {
+ assertThat(it.getElementsByTag("td")[6].text(), equalTo("740.9ms"))
+ }
+ }
+
+ @Test
+ fun `eighth column is numeric`() {
+ verifyColumnIsNumeric(7)
+ }
+
+ @Test
+ fun `eighth column contains 95th percentile`() {
+ createAndVerifyTableRow {
+ assertThat(it.getElementsByTag("td")[7].text(), equalTo("940.9ms"))
+ }
+ }
+
+ @Test
+ fun `ninth column is numeric`() {
+ verifyColumnIsNumeric(8)
+ }
+
+ @Test
+ fun `ninth column contains 98th percentile`() {
+ createAndVerifyTableRow {
+ assertThat(it.getElementsByTag("td")[8].text(), equalTo("975.6ms"))
+ }
+ }
+
+ @Test
+ fun `tenth column is numeric`() {
+ verifyColumnIsNumeric(9)
+ }
+
+ @Test
+ fun `tenth column contains 99th percentile`() {
+ createAndVerifyTableRow {
+ assertThat(it.getElementsByTag("td")[9].text(), equalTo("991.6ms"))
+ }
+ }
+
+ @Test
+ fun `eleventh column is numeric`() {
+ verifyColumnIsNumeric(10)
+ }
+
+ @Test
+ fun `eleventh column contains 99,9th percentile`() {
+ createAndVerifyTableRow {
+ assertThat(it.getElementsByTag("td")[10].text(), equalTo("998.0ms"))
+ }
+ }
+
+ private fun createAndVerifyTableRow(parameters: Map<String, Any?>? = null, verify: (Element) -> Unit) =
+ metricRenderer.format(templateContext, histogram, parameters)
+ .let { "<table id='t'>$it</table>" }
+ .let(Jsoup::parseBodyFragment)
+ .getElementById("t").child(0).child(0)
+ .let(verify)
+
+ private fun verifyColumnIsNumeric(column: Int) =
+ createAndVerifyTableRow {
+ assertThat(it.getElementsByTag("td")[column].classNames(), hasItem("numeric"))
+ }
+
+}
+
+private val random = Random(1)
+private val histogram = MetricRegistry().histogram("test.histogram") { Histogram(SlidingWindowReservoir(1028)) }.apply {
+ (0..2000).map { random.nextInt(1_000_000) }.forEach(this::update)
+}
import net.pterodactylus.sone.data.SoneOptions.LoadExternalContent.TRUSTED
import net.pterodactylus.sone.freenet.wot.OwnIdentity
import net.pterodactylus.sone.freenet.wot.Trust
-import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.test.*
import net.pterodactylus.sone.text.FreenetLinkPart
import net.pterodactylus.sone.text.LinkPart
import net.pterodactylus.sone.text.Part
import org.hamcrest.Matchers.emptyIterable
import org.junit.Before
import org.junit.Test
-import org.mockito.Mockito.`when`
/**
* Unit test for [LinkedElementsFilter].
@Before
fun setupSone() {
- `when`(sone.options).thenReturn(DefaultSoneOptions())
+ whenever(sone.options).thenReturn(DefaultSoneOptions())
}
@Before
fun setupImageLoader() {
- `when`(imageLoader.loadElement("KSK@link")).thenReturn(LinkedElement("KSK@link", failed = true))
- `when`(imageLoader.loadElement("KSK@loading.png")).thenReturn(LinkedElement("KSK@loading.png", loading = true))
- `when`(imageLoader.loadElement("KSK@link.png")).thenReturn(LinkedElement("KSK@link.png"))
+ whenever(imageLoader.loadElement("KSK@link")).thenReturn(LinkedElement("KSK@link", failed = true))
+ whenever(imageLoader.loadElement("KSK@loading.png")).thenReturn(LinkedElement("KSK@loading.png", loading = true))
+ whenever(imageLoader.loadElement("KSK@link.png")).thenReturn(LinkedElement("KSK@link.png"))
}
@Test
fun `filter finds images if the remote sone is local`() {
sone.options.loadLinkedImages = MANUALLY_TRUSTED
templateContext.set("currentSone", sone)
- `when`(remoteSone.isLocal).thenReturn(true)
+ whenever(remoteSone.isLocal).thenReturn(true)
parameters["sone"] = remoteSone
verifyThatImagesArePresent()
}
fun `filter does not find images if local sone requires manual trust and remote sone has only implicit trust`() {
sone.options.loadLinkedImages = MANUALLY_TRUSTED
templateContext.set("currentSone", sone)
- `when`(remoteSone.identity.getTrust(this.sone.identity as OwnIdentity)).thenReturn(Trust(null, 100, null))
+ whenever(remoteSone.identity.getTrust(this.sone.identity as OwnIdentity)).thenReturn(Trust(null, 100, null))
parameters["sone"] = remoteSone
verifyThatImagesAreNotPresent()
}
fun `filter does not find images if local sone requires manual trust and remote sone has explicit trust of zero`() {
sone.options.loadLinkedImages = MANUALLY_TRUSTED
templateContext.set("currentSone", sone)
- `when`(remoteSone.identity.getTrust(this.sone.identity as OwnIdentity)).thenReturn(Trust(0, null, null))
+ whenever(remoteSone.identity.getTrust(this.sone.identity as OwnIdentity)).thenReturn(Trust(0, null, null))
parameters["sone"] = remoteSone
verifyThatImagesAreNotPresent()
}
fun `filter finds images if local sone requires manual trust and remote sone has explicit trust of one`() {
sone.options.loadLinkedImages = MANUALLY_TRUSTED
templateContext.set("currentSone", sone)
- `when`(remoteSone.identity.getTrust(this.sone.identity as OwnIdentity)).thenReturn(Trust(1, null, null))
+ whenever(remoteSone.identity.getTrust(this.sone.identity as OwnIdentity)).thenReturn(Trust(1, null, null))
parameters["sone"] = remoteSone
verifyThatImagesArePresent()
}
@Test
fun `filter finds images if local sone requires following and remote sone is followed`() {
sone.options.loadLinkedImages = FOLLOWED
- `when`(sone.hasFriend("remote-id")).thenReturn(true)
+ whenever(sone.hasFriend("remote-id")).thenReturn(true)
templateContext["currentSone"] = sone
parameters["sone"] = remoteSone
verifyThatImagesArePresent()
fun `filter finds images if following is required and remote sone is a local sone`() {
sone.options.loadLinkedImages = FOLLOWED
templateContext["currentSone"] = sone
- `when`(remoteSone.isLocal).thenReturn(true)
+ whenever(remoteSone.isLocal).thenReturn(true)
parameters["sone"] = remoteSone
verifyThatImagesArePresent()
}
fun `filter does not find images if any trust is required and remote sone has implicit trust of zero`() {
sone.options.loadLinkedImages = TRUSTED
templateContext["currentSone"] = sone
- `when`(remoteSone.identity.getTrust(sone.identity as OwnIdentity)).thenReturn(Trust(null, 0, null))
+ whenever(remoteSone.identity.getTrust(sone.identity as OwnIdentity)).thenReturn(Trust(null, 0, null))
parameters["sone"] = remoteSone
verifyThatImagesAreNotPresent()
}
fun `filter finds images if any trust is required and remote sone has implicit trust of one`() {
sone.options.loadLinkedImages = TRUSTED
templateContext["currentSone"] = sone
- `when`(remoteSone.identity.getTrust(sone.identity as OwnIdentity)).thenReturn(Trust(null, 1, null))
+ whenever(remoteSone.identity.getTrust(sone.identity as OwnIdentity)).thenReturn(Trust(null, 1, null))
parameters["sone"] = remoteSone
verifyThatImagesArePresent()
}
fun `filter does not find images if any trust is required and remote sone has explicit trust of zero but implicit trust of one`() {
sone.options.loadLinkedImages = TRUSTED
templateContext["currentSone"] = sone
- `when`(remoteSone.identity.getTrust(sone.identity as OwnIdentity)).thenReturn(Trust(0, 1, null))
+ whenever(remoteSone.identity.getTrust(sone.identity as OwnIdentity)).thenReturn(Trust(0, 1, null))
parameters["sone"] = remoteSone
verifyThatImagesAreNotPresent()
}
fun `filter finds images if any trust is required and remote sone has explicit trust of one but no implicit trust`() {
sone.options.loadLinkedImages = TRUSTED
templateContext["currentSone"] = sone
- `when`(remoteSone.identity.getTrust(sone.identity as OwnIdentity)).thenReturn(Trust(1, null, null))
+ whenever(remoteSone.identity.getTrust(sone.identity as OwnIdentity)).thenReturn(Trust(1, null, null))
parameters["sone"] = remoteSone
verifyThatImagesArePresent()
}
fun `filter finds images if any trust is required and remote sone is a local sone`() {
sone.options.loadLinkedImages = TRUSTED
templateContext["currentSone"] = sone
- `when`(remoteSone.isLocal).thenReturn(true)
+ whenever(remoteSone.isLocal).thenReturn(true)
parameters["sone"] = remoteSone
verifyThatImagesArePresent()
}
private fun createSone(id: String = "sone-id"): Sone {
val sone = mock<Sone>()
- `when`(sone.id).thenReturn(id)
- `when`(sone.options).thenReturn(DefaultSoneOptions())
- `when`(sone.identity).thenReturn(mock<OwnIdentity>())
+ whenever(sone.id).thenReturn(id)
+ whenever(sone.options).thenReturn(DefaultSoneOptions())
+ whenever(sone.identity).thenReturn(mock<OwnIdentity>())
return sone
}
import com.google.inject.Guice
import net.pterodactylus.sone.core.Core
import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.getInstance
-import net.pterodactylus.sone.test.isProvidedByMock
-import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.test.*
import net.pterodactylus.sone.text.SoneTextParser
import net.pterodactylus.sone.text.SoneTextParserContext
import net.pterodactylus.util.template.TemplateContext
import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.`is`
+import org.hamcrest.Matchers.equalTo
import org.hamcrest.Matchers.emptyIterable
import org.hamcrest.Matchers.notNullValue
import org.hamcrest.Matchers.sameInstance
import org.junit.Test
import org.mockito.ArgumentCaptor.forClass
-import org.mockito.Mockito.`when`
import org.mockito.Mockito.eq
import org.mockito.Mockito.verify
private fun setupSone(identity: String): Sone {
val sone = mock<Sone>()
- `when`(sone.id).thenReturn(identity)
- `when`(core.getSone(identity)).thenReturn(sone)
+ whenever(sone.id).thenReturn(identity)
+ whenever(core.getSone(identity)).thenReturn(sone)
return sone
}
filter.format(templateContext, "text", parameters)
val context = forClass(SoneTextParserContext::class.java)
verify(soneTextParser).parse(eq<String>("text") ?: "", context.capture())
- assertThat(context.value.postingSone, `is`(sone))
+ assertThat(context.value.postingSone, equalTo(sone))
}
@Test
import net.pterodactylus.sone.test.whenever
import net.pterodactylus.util.template.TemplateContext
import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.`is`
+import org.hamcrest.Matchers.equalTo
import org.hamcrest.Matchers.nullValue
import org.junit.Before
import org.junit.Test
@Test
fun `avatar ID is returned if profile belongs to local sone`() {
whenever(remoteSone.isLocal).thenReturn(true)
- assertThat(accessor.get(templateContext, profile, "avatar"), `is`<Any>("avatar-id"))
+ assertThat(accessor.get(templateContext, profile, "avatar"), equalTo<Any>("avatar-id"))
}
@Test
@Test
fun `avatar ID is returned if sone is configure to always show avatars`() {
currentSone.options.showCustomAvatars = ALWAYS
- assertThat(accessor.get(templateContext, profile, "avatar"), `is`<Any>("avatar-id"))
+ assertThat(accessor.get(templateContext, profile, "avatar"), equalTo<Any>("avatar-id"))
}
@Test
fun `avatar ID is returned if sone is configure to show avatars of followed sones and remote sone is followed`() {
currentSone.options.showCustomAvatars = FOLLOWED
whenever(currentSone.hasFriend("remote-sone")).thenReturn(true)
- assertThat(accessor.get(templateContext, profile, "avatar"), `is`<Any>("avatar-id"))
+ assertThat(accessor.get(templateContext, profile, "avatar"), equalTo<Any>("avatar-id"))
}
@Test
fun `avatar ID is returned if sone is configure to show avatars based on manual trust and explicit trust is one`() {
currentSone.options.showCustomAvatars = MANUALLY_TRUSTED
setTrust(Trust(1, null, null))
- assertThat(accessor.get(templateContext, profile, "avatar"), `is`<Any>("avatar-id"))
+ assertThat(accessor.get(templateContext, profile, "avatar"), equalTo<Any>("avatar-id"))
}
@Test
fun `avatar ID is returned if sone is configure to show avatars based on trust and explicit trust is one`() {
currentSone.options.showCustomAvatars = TRUSTED
setTrust(Trust(1, null, null))
- assertThat(accessor.get(templateContext, profile, "avatar"), `is`<Any>("avatar-id"))
+ assertThat(accessor.get(templateContext, profile, "avatar"), equalTo<Any>("avatar-id"))
}
@Test
fun `avatar ID is returned if sone is configure to show avatars based on trust and implicit trust is one`() {
currentSone.options.showCustomAvatars = TRUSTED
setTrust(Trust(0, 1, null))
- assertThat(accessor.get(templateContext, profile, "avatar"), `is`<Any>("avatar-id"))
+ assertThat(accessor.get(templateContext, profile, "avatar"), equalTo<Any>("avatar-id"))
}
@Test
fun `accessing other members uses reflection accessor`() {
- assertThat(accessor.get(templateContext, profile, "hashCode"), `is`<Any>(profile.hashCode()))
+ assertThat(accessor.get(templateContext, profile, "hashCode"), equalTo<Any>(profile.hashCode()))
}
}
import net.pterodactylus.sone.data.Profile
import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.test.*
import net.pterodactylus.sone.text.FreenetLinkPart
import net.pterodactylus.sone.text.Part
import net.pterodactylus.sone.text.PlainTextPart
import org.hamcrest.MatcherAssert.assertThat
import org.hamcrest.Matchers.contains
import org.junit.Test
-import org.mockito.Mockito.`when`
/**
* Unit test for [ShortenFilter].
@Test
fun `sone parts are added but their length is ignored`() {
val sone = mock<Sone>()
- `when`(sone.profile).thenReturn(Profile(sone))
+ whenever(sone.profile).thenReturn(Profile(sone))
assertThat(shortenParts(15, 10, SonePart(sone), PlainTextPart("This is a long text.")), contains<Part>(
SonePart(sone),
PlainTextPart("This is a …")
@Test
fun `additional sone parts are ignored`() {
val sone = mock<Sone>()
- `when`(sone.profile).thenReturn(Profile(sone))
+ whenever(sone.profile).thenReturn(Profile(sone))
assertThat(shortenParts(15, 10, PlainTextPart("This is a long text."), SonePart(sone)), contains<Part>(
PlainTextPart("This is a …")
))
package net.pterodactylus.sone.template
-import freenet.l10n.BaseL10n
-import net.pterodactylus.sone.test.mock
-import net.pterodactylus.sone.test.whenever
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.junit.Test
+import net.pterodactylus.sone.freenet.*
+import net.pterodactylus.sone.test.*
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import org.junit.*
+import java.util.*
/**
* Unit test for [UnknownDateFilter].
*/
class UnknownDateFilterTest {
- private val baseL10n = mock<BaseL10n>()
+ private val translation = object : Translation {
+ override val currentLocale = Locale.ENGLISH
+ override fun translate(key: String) = if (key == unknownKey) "translated" else ""
+ }
private val unknownKey = "unknown.key"
- private val filter = UnknownDateFilter(baseL10n, unknownKey)
+ private val filter = UnknownDateFilter(translation, unknownKey)
@Test
fun `filter returns given object for non-longs`() {
- val someObject = Any()
+ val someObject = Any()
assertThat(filter.format(null, someObject, null), equalTo<Any>(someObject))
}
@Test
fun `filter returns translated value of unknown key if zero is given`() {
- whenever(baseL10n.getString(unknownKey)).thenReturn("translated")
assertThat(filter.format(null, 0L, null), equalTo<Any>("translated"))
}
package net.pterodactylus.sone.test
+import freenet.support.*
+import net.pterodactylus.sone.freenet.wot.*
+import net.pterodactylus.sone.utils.*
import net.pterodactylus.util.web.*
import org.hamcrest.*
+import org.hamcrest.Matchers.*
fun hasHeader(name: String, value: String) = object : TypeSafeDiagnosingMatcher<Header>() {
override fun matchesSafely(item: Header, mismatchDescription: Description) =
false.takeUnless { comparison(value) }
?.also { onError(value) }
+fun <K, V> isEmptyMap() = object : TypeSafeDiagnosingMatcher<Map<K, V>>() {
+ override fun describeTo(description: Description) {
+ description.appendText("empty map")
+ }
+
+ override fun matchesSafely(item: Map<K, V>, mismatchDescription: Description) =
+ item.isEmpty().onFalse {
+ mismatchDescription.appendText("was ").appendValue(item)
+ }
+}
+
+fun isTrust(trust: Int?, score: Int?, rank: Int?) =
+ AttributeMatcher<Trust>("trust")
+ .addAttribute("trust", trust, Trust::explicit)
+ .addAttribute("score", score, Trust::implicit)
+ .addAttribute("rank", rank, Trust::distance)
+
+fun isTrusted(ownIdentity: OwnIdentity, trust: Matcher<Trust>) = object : TypeSafeDiagnosingMatcher<Identity>() {
+ override fun matchesSafely(item: Identity, mismatchDescription: Description) =
+ item.getTrust(ownIdentity)?.let { foundTrust ->
+ trust.matches(foundTrust).onFalse {
+ trust.describeMismatch(foundTrust, mismatchDescription)
+ }
+ } ?: {
+ mismatchDescription.appendText("not trusted")
+ false
+ }()
+
+ override fun describeTo(description: Description) {
+ description
+ .appendText("trusted by ").appendValue(ownIdentity)
+ .appendText(" with ").appendValue(trust)
+ }
+}
+
+fun isIdentity(id: String, nickname: String?, requestUri: String, contexts: Matcher<out Iterable<String>>, properties: Matcher<out Map<out String, String>>) =
+ AttributeMatcher<Identity>("identity")
+ .addAttribute("id", id, Identity::getId)
+ .addAttribute("nickname", nickname, Identity::getNickname)
+ .addAttribute("requestUri", requestUri, Identity::getRequestUri)
+ .addAttribute("contexts", Identity::getContexts, contexts)
+ .addAttribute("properties", Identity::getProperties, properties)
+
+fun isOwnIdentity(id: String, nickname: String, requestUri: String, insertUri: String, contexts: Matcher<Iterable<String>>, properties: Matcher<Map<out String, String>>) =
+ AttributeMatcher<OwnIdentity>("own identity")
+ .addAttribute("id", id, OwnIdentity::getId)
+ .addAttribute("nickname", nickname, OwnIdentity::getNickname)
+ .addAttribute("request uri", requestUri, OwnIdentity::getRequestUri)
+ .addAttribute("insert uri", insertUri, OwnIdentity::getInsertUri)
+ .addAttribute("contexts", OwnIdentity::getContexts, contexts)
+ .addAttribute("properties", OwnIdentity::getProperties, properties)
+
+fun hasField(name: String, valueMatcher: Matcher<String>) = object : TypeSafeDiagnosingMatcher<SimpleFieldSet>() {
+ override fun matchesSafely(item: SimpleFieldSet, mismatchDescription: Description) =
+ valueMatcher.matches(item.get(name)).onFalse {
+ valueMatcher.describeMismatch(item, mismatchDescription)
+ }
+
+ override fun describeTo(description: Description) {
+ description
+ .appendText("simple field set with key ").appendValue(name)
+ .appendText(", value ").appendValue(valueMatcher)
+ }
+}
+
+/**
+ * [TypeSafeDiagnosingMatcher] implementation that aims to cut down boilerplate on verifying the attributes
+ * of typical container objects.
+ */
+class AttributeMatcher<T>(private val objectName: String) : TypeSafeDiagnosingMatcher<T>() {
+
+ private data class AttributeToMatch<T, V>(
+ val name: String,
+ val getter: (T) -> V,
+ val matcher: Matcher<out V>
+ )
+
+ private val attributesToMatch = mutableListOf<AttributeToMatch<T, *>>()
+
+ /**
+ * Adds an attribute to check for equality, returning `this`.
+ */
+ fun <V> addAttribute(name: String, expected: V, getter: (T) -> V): AttributeMatcher<T> = apply {
+ attributesToMatch.add(AttributeToMatch(name, getter, describedAs("$name %0", equalTo(expected), expected)))
+ }
+
+ /**
+ * Adds an attribute to check with the given [hamcrest matcher][Matcher].
+ */
+ fun <V> addAttribute(name: String, getter: (T) -> V, matcher: Matcher<out V>) = apply {
+ attributesToMatch.add(AttributeToMatch(name, getter, matcher))
+ }
+
+ override fun describeTo(description: Description) {
+ attributesToMatch.forEachIndexed { index, attributeToMatch ->
+ if (index == 0) {
+ description.appendText("$objectName with ")
+ } else {
+ description.appendText(", ")
+ }
+ attributeToMatch.matcher.describeTo(description)
+ }
+ }
+
+ override fun matchesSafely(item: T, mismatchDescription: Description): Boolean =
+ attributesToMatch.fold(true) { matches, attributeToMatch ->
+ if (!matches) {
+ false
+ } else {
+ if (!attributeToMatch.matcher.matches(attributeToMatch.getter(item))) {
+ mismatchDescription.appendText("but ${attributeToMatch.name} ")
+ attributeToMatch.matcher.describeMismatch(attributeToMatch.getter(item), mismatchDescription)
+ false
+ } else {
+ true
+ }
+ }
+ }
+
+}
import com.google.inject.Module
import org.mockito.*
import org.mockito.invocation.InvocationOnMock
-import org.mockito.stubbing.OngoingStubbing
+import org.mockito.stubbing.*
inline fun <reified T : Any> mock(): T = Mockito.mock<T>(T::class.java)!!
inline fun <reified T : Any> mockBuilder(): T = Mockito.mock<T>(T::class.java, Mockito.RETURNS_SELF)!!
Module { it!!.bind(T::class.java).toInstance(mock()) }
inline fun <reified T: Any?> whenever(methodCall: T) = Mockito.`when`(methodCall)!!
+inline fun <reified T: Any?> Stubber.whenever(mock: T) = `when`(mock)!!
inline fun <reified T : Any> OngoingStubbing<T>.thenReturnMock(): OngoingStubbing<T> = this.thenReturn(mock())
package net.pterodactylus.sone.text
import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.`is`
+import org.hamcrest.Matchers.equalTo
import org.junit.Test
/**
@Test
fun linkIsUsedAsTitleIfNoTextIsGiven() {
- assertThat(FreenetLinkPart("link", "text", true).title, `is`("link"))
+ assertThat(FreenetLinkPart("link", "text", true).title, equalTo("link"))
}
}
package net.pterodactylus.sone.text
import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.`is`
+import org.hamcrest.Matchers.equalTo
import org.junit.Test
/**
@Test
fun linkIsUsedAsTitleIfNoTitleIsGiven() {
- assertThat(LinkPart("link", "text").title, `is`("link"))
+ assertThat(LinkPart("link", "text").title, equalTo("link"))
}
}
package net.pterodactylus.sone.text
-import net.pterodactylus.sone.data.Profile
import net.pterodactylus.sone.data.Sone
-import net.pterodactylus.sone.test.mock
+import net.pterodactylus.sone.test.*
import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.`is`
+import org.hamcrest.Matchers.equalTo
import org.junit.Test
-import org.mockito.Mockito.`when`
/**
* Unit test for [SonePart].
private val sone = mock<Sone>()
init {
- `when`(sone.profile).thenReturn(mock())
- `when`(sone.name).thenReturn("sone")
+ whenever(sone.profile).thenReturn(mock())
+ whenever(sone.name).thenReturn("sone")
}
private val part = SonePart(sone)
@Test
fun textIsConstructedFromSonesNiceName() {
- assertThat<String>(part.text, `is`<String>("sone"))
+ assertThat<String>(part.text, equalTo<String>("sone"))
}
}
package net.pterodactylus.sone.utils
-import org.hamcrest.MatcherAssert.assertThat
-import org.hamcrest.Matchers.equalTo
-import org.hamcrest.Matchers.nullValue
-import org.junit.Test
+import org.hamcrest.MatcherAssert.*
+import org.hamcrest.Matchers.*
+import kotlin.test.*
/**
* Unit test for [Booleans].
assertThat(true.ifFalse { true }, nullValue())
}
+ @Test
+ fun `onFalse returns true on true`() {
+ assertThat(true.onFalse {}, equalTo(true))
+ }
+
+ @Test
+ fun `onFalse returns false on false`() {
+ assertThat(false.onFalse {}, equalTo(false))
+ }
+
+ @Test
+ fun `onFalse is not executed on true`() {
+ assertThat(true.onFalse { throw RuntimeException() }, equalTo(true))
+ }
+
+ @Test(expected = RuntimeException::class)
+ fun `onFalse is executed on false`() {
+ false.onFalse { throw RuntimeException() }
+ }
+
}
import org.hamcrest.MatcherAssert.*
import org.hamcrest.Matchers.*
import org.junit.*
+import java.util.*
import kotlin.test.Test
class WebInterfaceModuleTest {
private val webInterfaceModule = WebInterfaceModule()
- private val l10n = mock<BaseL10n>()
private val loaders = mock<Loaders>()
+ private val translation = object : Translation {
+ override val currentLocale = Locale.ENGLISH
+ override fun translate(key: String) = if (key == "View.Sone.Text.UnknownDate") "unknown" else key
+ }
private val additionalModules = arrayOf(
Core::class.isProvidedByMock(),
SoneProvider::class.isProvidedByMock(),
- BaseL10n::class.isProvidedBy(l10n),
+ Translation::class.isProvidedBy(translation),
SoneTextParser::class.isProvidedByMock(),
ElementLoader::class.isProvidedByMock(),
Loaders::class.isProvidedBy(loaders),
@Test
fun `unknown date filter uses correct l10n key`() {
- whenever(l10n.getString("View.Sone.Text.UnknownDate")).thenReturn("unknown")
assertThat(getFilter("unknown")!!.format(null, 0L, emptyMap()), equalTo<Any>("unknown"))
}
verifyFilter<PaginationFilter>("paginate")
}
+ @Test
+ fun `template context histogram renderer`() {
+ verifyFilter<HistogramRenderer>("render-histogram")
+ }
+
private inline fun <reified F : Filter> verifyFilter(name: String) {
assertThat(getFilter(name), instanceOf(F::class.java))
}
import com.fasterxml.jackson.databind.ObjectMapper
import com.google.common.eventbus.EventBus
import freenet.clients.http.ToadletContext
-import freenet.l10n.BaseL10n
import freenet.support.SimpleReadOnlyArrayBucket
import freenet.support.api.HTTPRequest
import net.pterodactylus.sone.core.Core
import net.pterodactylus.sone.data.Sone.SoneStatus
import net.pterodactylus.sone.data.Sone.SoneStatus.idle
import net.pterodactylus.sone.data.SoneOptions.DefaultSoneOptions
+import net.pterodactylus.sone.freenet.*
import net.pterodactylus.sone.test.deepMock
import net.pterodactylus.sone.test.get
import net.pterodactylus.sone.test.mock
import net.pterodactylus.util.web.Method.GET
import net.pterodactylus.util.web.Method.POST
import org.mockito.ArgumentMatchers
-import java.util.NoSuchElementException
+import java.util.*
import javax.naming.SizeLimitExceededException
/**
val webInterface = mock<WebInterface>()
var formPassword = "form-password"
- val l10n = mock<BaseL10n>()
val core = mock<Core>()
val eventBus = mock<EventBus>()
val preferences = Preferences(eventBus)
val images = mutableMapOf<String, Image>()
val translations = mutableMapOf<String, String>()
+ private val translation = object : Translation {
+ override val currentLocale = Locale.ENGLISH
+ override fun translate(key: String) = translations[key] ?: ""
+ }
+
init {
whenever(webInterface.templateContextFactory).thenReturn(TemplateContextFactory())
whenever(webInterface.getCurrentSone(ArgumentMatchers.eq(toadletContext), ArgumentMatchers.anyBoolean())).thenReturn(currentSone)
whenever(webInterface.getNotification(ArgumentMatchers.anyString())).then { notifications[it[0]].asOptional() }
whenever(webInterface.getNewPosts(currentSone)).thenAnswer { newPosts.values }
whenever(webInterface.getNewReplies(currentSone)).thenAnswer { newReplies.values }
- whenever(webInterface.l10n).thenReturn(l10n)
-
- whenever(l10n.getString(ArgumentMatchers.anyString())).then { translations[it[0]] }
+ whenever(webInterface.translation).thenReturn(translation)
whenever(core.preferences).thenReturn(preferences)
whenever(core.updateChecker).thenReturn(updateChecker)
import freenet.clients.http.*
import freenet.clients.http.SessionManager.*
-import freenet.l10n.*
import freenet.support.api.*
import net.pterodactylus.sone.test.*
import net.pterodactylus.util.web.*
private val method = Method.GET
private val httpRequest = mock(HTTPRequest::class.java)
private val toadletContext = mock(ToadletContext::class.java)
- private val l10n = mock<BaseL10n>()
private val sessionManager = mock<SessionManager>()
- private val request = FreenetRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager)
+ private val request = FreenetRequest(uri, method, httpRequest, toadletContext, sessionManager)
@Test
fun `uri is retained correctly`() {
}
@Test
- fun `l10n is retained correctly`() {
- assertThat(request.l10n, equalTo(l10n))
- }
-
- @Test
fun `null is returned if no session exists`() {
assertThat(request.existingSession, nullValue())
}
private val method = Method.GET
private val httpRequest = Mockito.mock(HTTPRequest::class.java)
private val toadletContext = Mockito.mock(ToadletContext::class.java)
- private val l10n = mock<BaseL10n>()
private val sessionManager = mock<SessionManager>()
private val core = mock<Core>()
private val webInterface = mock<WebInterface>()
- private val soneRequest = SoneRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager, core, webInterface)
+ private val soneRequest = SoneRequest(uri, method, httpRequest, toadletContext, sessionManager, core, webInterface)
@Test
fun `freenet request properties are retained correctly`() {
assertThat(soneRequest.method, equalTo(method))
assertThat(soneRequest.httpRequest, equalTo(httpRequest))
assertThat(soneRequest.toadletContext, equalTo(toadletContext))
- assertThat(soneRequest.l10n, equalTo(l10n))
assertThat(soneRequest.sessionManager, equalTo(sessionManager))
}
@Test
fun `freenet request is wrapped correctly`() {
- val freenetRequest = FreenetRequest(uri, method, httpRequest, toadletContext, l10n, sessionManager)
+ val freenetRequest = FreenetRequest(uri, method, httpRequest, toadletContext, sessionManager)
val wrappedSoneRequest = freenetRequest.toSoneRequest(core, webInterface)
assertThat(wrappedSoneRequest.uri, equalTo(uri))
assertThat(wrappedSoneRequest.method, equalTo(method))
assertThat(wrappedSoneRequest.httpRequest, equalTo(httpRequest))
assertThat(wrappedSoneRequest.toadletContext, equalTo(toadletContext))
- assertThat(wrappedSoneRequest.l10n, equalTo(l10n))
assertThat(wrappedSoneRequest.sessionManager, equalTo(sessionManager))
assertThat(wrappedSoneRequest.core, sameInstance(core))
assertThat(wrappedSoneRequest.webInterface, sameInstance(webInterface))
@Test
fun `page returns correct title`() {
- whenever(l10n.getString("Page.DeleteSone.Title")).thenReturn("delete sone page")
+ addTranslation("Page.DeleteSone.Title", "delete sone page")
assertThat(page.getPageTitle(soneRequest), equalTo("delete sone page"))
}
@Test
fun `page returns correct title`() {
- whenever(l10n.getString("Page.DismissNotification.Title")).thenReturn("dismiss notification page")
+ addTranslation("Page.DismissNotification.Title", "dismiss notification page")
assertThat(page.getPageTitle(soneRequest), equalTo("dismiss notification page"))
}
@Test
fun `page returns correct title`() {
- whenever(l10n.getString("Page.Distrust.Title")).thenReturn("distrust page title")
+ addTranslation("Page.Distrust.Title", "distrust page title")
assertThat(page.getPageTitle(soneRequest), equalTo("distrust page title"))
}
@Test
fun `page returns correct title`() {
- whenever(l10n.getString("Page.EditAlbum.Title")).thenReturn("edit album page")
+ addTranslation("Page.EditAlbum.Title", "edit album page")
assertThat(page.getPageTitle(soneRequest), equalTo("edit album page"))
}
@Test
fun `page returns correct title`() {
- whenever(l10n.getString("Page.EditImage.Title")).thenReturn("edit image page title")
+ addTranslation("Page.EditImage.Title", "edit image page title")
assertThat(page.getPageTitle(soneRequest), equalTo("edit image page title"))
}
@Test
fun `page returns correct title`() {
- whenever(l10n.getString("Page.EditProfileField.Title")).thenReturn("edit profile field title")
+ addTranslation("Page.EditProfileField.Title", "edit profile field title")
assertThat(page.getPageTitle(soneRequest), equalTo("edit profile field title"))
}
@Test
fun `page returns correct title`() {
- whenever(l10n.getString("Page.EditProfile.Title")).thenReturn("edit profile page title")
+ addTranslation("Page.EditProfile.Title", "edit profile page title")
assertThat(page.getPageTitle(soneRequest), equalTo("edit profile page title"))
}
@Test
fun `page returns correct title`() {
- whenever(l10n.getString("Page.FollowSone.Title")).thenReturn("follow sone page title")
+ addTranslation("Page.FollowSone.Title", "follow sone page title")
assertThat(page.getPageTitle(soneRequest), equalTo("follow sone page title"))
}
@Test
fun `page returns correct title`() {
- whenever(l10n.getString("Page.ImageBrowser.Title")).thenReturn("image browser page title")
+ addTranslation("Page.ImageBrowser.Title", "image browser page title")
assertThat(page.getPageTitle(soneRequest), equalTo("image browser page title"))
}
@Test
fun `page returns correct title`() {
- whenever(l10n.getString("Page.Index.Title")).thenReturn("index page title")
+ addTranslation("Page.Index.Title", "index page title")
assertThat(page.getPageTitle(soneRequest), equalTo("index page title"))
}
@Test
fun `page returns correct title`() {
- whenever(l10n.getString("Page.KnownSones.Title")).thenReturn("known sones page title")
+ addTranslation("Page.KnownSones.Title", "known sones page title")
assertThat(page.getPageTitle(soneRequest), equalTo("known sones page title"))
}
}
@Test
- fun `metrics page lists stats about sone parsing durations`() {
- createHistogram("sone.parsing.duration")
+ @Suppress("UNCHECKED_CAST")
+ fun `metrics page stores histograms in template context`() {
+ createHistogram("sone.random.duration2")
+ createHistogram("sone.random.duration1")
page.handleRequest(soneRequest, templateContext)
- verifyHistogram("soneParsingDuration")
- }
-
- @Test
- fun `metrice pags lists stats about sone insert durations`() {
- createHistogram("sone.insert.duration")
- page.handleRequest(soneRequest, templateContext)
- verifyHistogram("soneInsertDuration")
- }
-
- @Test
- fun `metrics page delivers correct histogram size`() {
- val histogram = metricRegistry.histogram("sone.parsing.duration")
- (0..4000).forEach(histogram::update)
- page.handleRequest(soneRequest, templateContext)
- assertThat(templateContext["soneParsingDurationCount"] as Long, equalTo(4001L))
- }
-
- private fun verifyHistogram(name: String) {
- assertThat(templateContext["${name}Count"] as Long, equalTo(5L))
- assertThat(templateContext["${name}Min"] as Long, equalTo(1L))
- assertThat(templateContext["${name}Max"] as Long, equalTo(10L))
- assertThat(templateContext["${name}Median"] as Double, equalTo(8.0))
- assertThat(templateContext["${name}Percentile75"] as Double, equalTo(9.0))
- assertThat(templateContext["${name}Percentile95"] as Double, equalTo(10.0))
- assertThat(templateContext["${name}Percentile98"] as Double, equalTo(10.0))
- assertThat(templateContext["${name}Percentile99"] as Double, equalTo(10.0))
- assertThat(templateContext["${name}Percentile999"] as Double, equalTo(10.0))
+ val histograms = templateContext["histograms"] as Map<String, Histogram>
+ assertThat(histograms.entries.map { it.key to it.value }, containsInAnyOrder(
+ "sone.random.duration1" to metricRegistry.histogram("sone.random.duration1"),
+ "sone.random.duration2" to metricRegistry.histogram("sone.random.duration2")
+ ))
}
private fun createHistogram(name: String) = metricRegistry.histogram(name).run {
}
@Test
- fun `post request with fetch starts next fetch`() {
+ fun `post request with fetch and invalid edition starts next fetch`() {
setMethod(POST)
addHttpRequestPart("fetch", "true")
verifyRedirect("rescue.html") {
+ verify(soneRescuer, never()).setEdition(anyLong())
+ verify(soneRescuer).startNextFetch()
+ }
+ }
+
+ @Test
+ fun `post request with fetch and valid edition sets edition and starts next fetch`() {
+ setMethod(POST)
+ addHttpRequestPart("fetch", "true")
+ addHttpRequestPart("edition", "123")
+ verifyRedirect("rescue.html") {
+ verify(soneRescuer).setEdition(123L)
+ verify(soneRescuer).startNextFetch()
+ }
+ }
+
+ @Test
+ fun `post request with negative edition will not set edition`() {
+ setMethod(POST)
+ addHttpRequestPart("fetch", "true")
+ addHttpRequestPart("edition", "-123")
+ verifyRedirect("rescue.html") {
+ verify(soneRescuer, never()).setEdition(anyLong())
verify(soneRescuer).startNextFetch()
}
}
@Test
fun `page title is retrieved from l10n if page title key is given`() {
SoneTemplatePage(webInterface, loaders, templateRenderer, pageTitleKey = "page.title", requiresLogin = false).let { page ->
- whenever(l10n.getString("page.title")).thenReturn("Page Title")
+ addTranslation("page.title", "Page Title")
assertThat(page.getPageTitle(soneRequest), equalTo("Page Title"))
}
}
import freenet.support.api.*
import net.pterodactylus.sone.core.*
import net.pterodactylus.sone.data.*
+import net.pterodactylus.sone.freenet.*
import net.pterodactylus.sone.freenet.wot.*
import net.pterodactylus.sone.main.*
import net.pterodactylus.sone.test.deepMock
import java.io.*
import java.net.*
import java.nio.charset.*
+import java.util.*
import kotlin.text.Charsets.UTF_8
/**
val core = webInterface.core
val eventBus = mock<EventBus>()
val preferences = Preferences(eventBus)
- val l10n = webInterface.l10n!!
val sessionManager = mock<SessionManager>()
open val page by lazy { pageSupplier(webInterface, loaders, templateRenderer) }
val freenetRequest = mock<FreenetRequest>()
init {
- whenever(freenetRequest.l10n).thenReturn(l10n)
whenever(freenetRequest.sessionManager).thenReturn(sessionManager)
whenever(freenetRequest.uri).thenReturn(mock())
}
private val notifications = mutableMapOf<String, Notification>()
private val translations = mutableMapOf<String, String>()
+ private val translation = object : Translation {
+ override val currentLocale = Locale.ENGLISH
+ override fun translate(key: String) = translations[key] ?: key
+ }
+
init {
setupCore()
setupWebInterface()
setupHttpRequest()
setupFreenetRequest()
- setupTranslations()
}
private fun setupCore() {
whenever(webInterface.getCurrentSoneWithoutCreatingSession(eq(toadletContext))).thenReturn(currentSone)
whenever(webInterface.getNotifications(currentSone)).then { notifications.values }
whenever(webInterface.getNotification(anyString())).then { notifications[it[0]].asOptional() }
+ whenever(webInterface.translation).thenReturn(translation)
}
private fun setupHttpRequest() {
whenever(freenetRequest.toadletContext).thenReturn(toadletContext)
}
- private fun setupTranslations() {
- whenever(l10n.getString(anyString())).then { translations[it[0]] ?: it[0] }
- }
-
fun setMethod(method: Method) {
whenever(httpRequest.method).thenReturn(method.name)
whenever(freenetRequest.method).thenReturn(method)