Merge branch 'release-0.9.6' 0.9.6
authorDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 26 Oct 2016 16:48:04 +0000 (18:48 +0200)
committerDavid ‘Bombe’ Roden <bombe@pterodactylus.net>
Wed, 26 Oct 2016 16:48:04 +0000 (18:48 +0200)
115 files changed:
build.gradle [new file with mode: 0644]
gradle/wrapper/gradle-wrapper.jar [new file with mode: 0644]
gradle/wrapper/gradle-wrapper.properties [new file with mode: 0644]
gradlew [new file with mode: 0755]
gradlew.bat [new file with mode: 0644]
pom.xml [deleted file]
settings.gradle [new file with mode: 0644]
src/main/java/net/pterodactylus/sone/core/Core.java
src/main/java/net/pterodactylus/sone/core/SoneInserter.java
src/main/java/net/pterodactylus/sone/data/Post.java
src/main/java/net/pterodactylus/sone/data/impl/AbstractPostBuilder.java
src/main/java/net/pterodactylus/sone/data/impl/AbstractPostReplyBuilder.java
src/main/java/net/pterodactylus/sone/data/impl/PostReplyBuilderImpl.java
src/main/java/net/pterodactylus/sone/data/impl/SoneImpl.java
src/main/java/net/pterodactylus/sone/fcp/GetPostFeedCommand.java
src/main/java/net/pterodactylus/sone/fcp/VersionCommand.java
src/main/java/net/pterodactylus/sone/main/SonePlugin.java
src/main/java/net/pterodactylus/sone/template/ImageLinkFilter.java
src/main/java/net/pterodactylus/sone/template/ParserFilter.java
src/main/java/net/pterodactylus/sone/text/FreenetLinkPart.java
src/main/java/net/pterodactylus/sone/text/LinkPart.java
src/main/java/net/pterodactylus/sone/text/PartContainer.java
src/main/java/net/pterodactylus/sone/text/PlainTextPart.java
src/main/java/net/pterodactylus/sone/text/PostPart.java
src/main/java/net/pterodactylus/sone/text/SonePart.java
src/main/java/net/pterodactylus/sone/text/SoneTextParser.java
src/main/java/net/pterodactylus/sone/text/SoneTextParserContext.java
src/main/java/net/pterodactylus/sone/web/AboutPage.java
src/main/java/net/pterodactylus/sone/web/BookmarkPage.java
src/main/java/net/pterodactylus/sone/web/BookmarksPage.java
src/main/java/net/pterodactylus/sone/web/CreateAlbumPage.java
src/main/java/net/pterodactylus/sone/web/CreatePostPage.java
src/main/java/net/pterodactylus/sone/web/CreateReplyPage.java
src/main/java/net/pterodactylus/sone/web/CreateSonePage.java
src/main/java/net/pterodactylus/sone/web/DeleteAlbumPage.java
src/main/java/net/pterodactylus/sone/web/DeleteImagePage.java
src/main/java/net/pterodactylus/sone/web/DeletePostPage.java
src/main/java/net/pterodactylus/sone/web/DeleteProfileFieldPage.java
src/main/java/net/pterodactylus/sone/web/DeleteReplyPage.java
src/main/java/net/pterodactylus/sone/web/DeleteSonePage.java
src/main/java/net/pterodactylus/sone/web/DismissNotificationPage.java
src/main/java/net/pterodactylus/sone/web/DistrustPage.java
src/main/java/net/pterodactylus/sone/web/EditAlbumPage.java
src/main/java/net/pterodactylus/sone/web/EditImagePage.java
src/main/java/net/pterodactylus/sone/web/EditProfileFieldPage.java
src/main/java/net/pterodactylus/sone/web/EditProfilePage.java
src/main/java/net/pterodactylus/sone/web/FollowSonePage.java
src/main/java/net/pterodactylus/sone/web/ImageBrowserPage.java
src/main/java/net/pterodactylus/sone/web/IndexPage.java
src/main/java/net/pterodactylus/sone/web/KnownSonesPage.java
src/main/java/net/pterodactylus/sone/web/LikePage.java
src/main/java/net/pterodactylus/sone/web/LockSonePage.java
src/main/java/net/pterodactylus/sone/web/LoginPage.java
src/main/java/net/pterodactylus/sone/web/LogoutPage.java
src/main/java/net/pterodactylus/sone/web/MarkAsKnownPage.java
src/main/java/net/pterodactylus/sone/web/NewPage.java
src/main/java/net/pterodactylus/sone/web/OptionsPage.java
src/main/java/net/pterodactylus/sone/web/RescuePage.java
src/main/java/net/pterodactylus/sone/web/SearchPage.java
src/main/java/net/pterodactylus/sone/web/SoneTemplatePage.java
src/main/java/net/pterodactylus/sone/web/TrustPage.java
src/main/java/net/pterodactylus/sone/web/UnbookmarkPage.java
src/main/java/net/pterodactylus/sone/web/UnfollowSonePage.java
src/main/java/net/pterodactylus/sone/web/UnlikePage.java
src/main/java/net/pterodactylus/sone/web/UnlockSonePage.java
src/main/java/net/pterodactylus/sone/web/UntrustPage.java
src/main/java/net/pterodactylus/sone/web/UploadImagePage.java
src/main/java/net/pterodactylus/sone/web/ViewPostPage.java
src/main/java/net/pterodactylus/sone/web/ViewSonePage.java
src/main/java/net/pterodactylus/sone/web/WebInterface.java
src/main/java/net/pterodactylus/sone/web/ajax/GetNotificationsAjaxPage.java
src/test/java/net/pterodactylus/sone/TestAlbumBuilder.java
src/test/java/net/pterodactylus/sone/core/ConfigurationSoneParserTest.java
src/test/java/net/pterodactylus/sone/core/CoreTest.java
src/test/java/net/pterodactylus/sone/core/FreenetInterfaceTest.java
src/test/java/net/pterodactylus/sone/core/ImageInserterTest.java
src/test/java/net/pterodactylus/sone/core/PreferencesTest.java
src/test/java/net/pterodactylus/sone/core/SoneChangeDetectorTest.java
src/test/java/net/pterodactylus/sone/core/SoneDownloaderTest.java
src/test/java/net/pterodactylus/sone/core/SoneInserterTest.java
src/test/java/net/pterodactylus/sone/core/SoneParserTest.java
src/test/java/net/pterodactylus/sone/core/SoneRescuerTest.java
src/test/java/net/pterodactylus/sone/core/UpdateCheckerTest.java
src/test/java/net/pterodactylus/sone/core/WebOfTrustUpdaterTest.java
src/test/java/net/pterodactylus/sone/database/memory/MemoryBookmarkDatabaseTest.java
src/test/java/net/pterodactylus/sone/database/memory/MemoryDatabaseTest.java
src/test/java/net/pterodactylus/sone/fcp/LockSoneCommandTest.java
src/test/java/net/pterodactylus/sone/fcp/UnlockSoneCommandTest.java
src/test/java/net/pterodactylus/sone/freenet/wot/IdentityChangeEventSenderTest.java
src/test/java/net/pterodactylus/sone/freenet/wot/IdentityLoaderTest.java
src/test/java/net/pterodactylus/sone/notify/ListNotificationFilterTest.java
src/test/java/net/pterodactylus/sone/notify/ListNotificationTest.java
src/test/java/net/pterodactylus/sone/template/ImageLinkFilterTest.java
src/test/java/net/pterodactylus/sone/test/Dirty.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/text/FreenetLinkPartTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/text/LinkPartTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/text/PartContainerTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/text/PlainTextPartTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/text/PostPartTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/text/SonePartTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java
src/test/java/net/pterodactylus/sone/web/AboutPageTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/web/BookmarkPageTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/web/BookmarksPageTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/web/CreateAlbumPageTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/web/CreatePostPageTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/web/CreateReplyPageTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/web/CreateSonePageTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/web/DeleteReplyPageTest.java
src/test/java/net/pterodactylus/sone/web/NewPageTest.java
src/test/java/net/pterodactylus/sone/web/UploadImagePageTest.java
src/test/java/net/pterodactylus/sone/web/WebPageTest.java [new file with mode: 0644]
src/test/java/net/pterodactylus/sone/web/ajax/BookmarkAjaxPageTest.java
src/test/java/net/pterodactylus/sone/web/page/FreenetRequestTest.java [new file with mode: 0644]
template.txt [deleted file]

diff --git a/build.gradle b/build.gradle
new file mode 100644 (file)
index 0000000..9221dd3
--- /dev/null
@@ -0,0 +1,94 @@
+group = 'net.pterodactylus'
+version = '0.9.6'
+
+buildscript {
+    repositories {
+        mavenCentral()
+    }
+    dependencies {
+        classpath group: 'info.solidsoft.gradle.pitest', name: 'gradle-pitest-plugin', version: '1.1.10'
+    }
+}
+
+repositories {
+     maven { url "http://maven.pterodactylus.net/" }
+     mavenCentral()
+}
+
+apply plugin: 'java'
+
+sourceCompatibility = 1.7
+targetCompatibility = 1.7
+
+tasks.withType(JavaCompile) {
+       options.encoding = 'UTF-8'
+}
+
+configurations {
+    provided {
+        dependencies.all { dep ->
+            configurations.default.exclude group: dep.group, module: dep.name
+        }
+    }
+    compile.extendsFrom provided
+}
+dependencies {
+    provided group: 'org.freenetproject', name: 'fred', version: '0.7.5.1475'
+    provided group: 'org.freenetproject', name: 'freenet-ext', version: '29'
+    provided group: 'org.bouncycastle', name: 'bcprov-jdk15on', version: '1.54'
+
+    compile group: 'net.pterodactylus', name: 'utils', version: '0.12.4'
+    compile group: 'com.google.inject', name: 'guice', version: '3.0'
+    compile group: 'com.google.guava', name: 'guava', version: '14.0.1'
+    compile group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: '2.1.2'
+    compile group: 'com.google.code.findbugs', name: 'jsr305', version: '2.0.1'
+
+    testCompile group: 'junit', name: 'junit', version: '4.11'
+    testCompile group: 'org.mockito', name: 'mockito-core', version: '2.1.0'
+    testCompile group: 'org.jsoup', name: 'jsoup', version: '1.7.1'
+    testCompile group: 'org.hamcrest', name: 'hamcrest-all', version: '1.3'
+}
+
+task fatJar(type: Jar) {
+    archiveName = project.name + '-jar-with-dependencies.jar'
+    from { (configurations.runtime - configurations.provided).collect { it.isDirectory() ? it : zipTree(it) } }
+    manifest {
+        attributes('Plugin-Main-Class': 'net.pterodactylus.sone.main.SonePlugin')
+    }
+    with jar
+}
+
+javadoc {
+    options {
+        quiet()
+        showFromPrivate()
+        footer('© 2010–2013 David ‘Bombe’ Roden')
+        links('http://docs.oracle.com/javase/7/docs/api/')
+    }
+    failOnError = false
+}
+
+apply plugin: 'jacoco'
+
+jacoco {
+    toolVersion = '0.7.7.201606060606'
+}
+
+jacocoTestReport.dependsOn test
+
+apply plugin: 'info.solidsoft.pitest'
+
+pitest {
+    outputFormats = ['HTML', 'XML']
+    timestampedReports = false
+    timeoutFactor = 3.0
+}
+
+apply plugin: 'findbugs'
+
+findbugs {
+    ignoreFailures = true
+}
+
+apply plugin: 'idea'
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644 (file)
index 0000000..6ffa237
Binary files /dev/null and b/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644 (file)
index 0000000..92781d4
--- /dev/null
@@ -0,0 +1,6 @@
+#Wed Oct 19 20:11:35 CEST 2016
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-3.1-bin.zip
diff --git a/gradlew b/gradlew
new file mode 100755 (executable)
index 0000000..9aa616c
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,169 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+##  Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+    ls=`ls -ld "$PRG"`
+    link=`expr "$ls" : '.*-> \(.*\)$'`
+    if expr "$link" : '/.*' > /dev/null; then
+        PRG="$link"
+    else
+        PRG=`dirname "$PRG"`"/$link"
+    fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+    echo "$*"
+}
+
+die ( ) {
+    echo
+    echo "$*"
+    echo
+    exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+  CYGWIN* )
+    cygwin=true
+    ;;
+  Darwin* )
+    darwin=true
+    ;;
+  MINGW* )
+    msys=true
+    ;;
+  NONSTOP* )
+    nonstop=true
+    ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+        JAVACMD="$JAVA_HOME/bin/java"
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD="java"
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+    MAX_FD_LIMIT=`ulimit -H -n`
+    if [ $? -eq 0 ] ; then
+        if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+            MAX_FD="$MAX_FD_LIMIT"
+        fi
+        ulimit -n $MAX_FD
+        if [ $? -ne 0 ] ; then
+            warn "Could not set maximum file descriptor limit: $MAX_FD"
+        fi
+    else
+        warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+    fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+    GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+    APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+    CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+    JAVACMD=`cygpath --unix "$JAVACMD"`
+
+    # We build the pattern for arguments to be converted via cygpath
+    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+    SEP=""
+    for dir in $ROOTDIRSRAW ; do
+        ROOTDIRS="$ROOTDIRS$SEP$dir"
+        SEP="|"
+    done
+    OURCYGPATTERN="(^($ROOTDIRS))"
+    # Add a user-defined pattern to the cygpath arguments
+    if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+        OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+    fi
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    i=0
+    for arg in "$@" ; do
+        CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+        CHECK2=`echo "$arg"|egrep -c "^-"`                                 ### Determine if an option
+
+        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition
+            eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+        else
+            eval `echo args$i`="\"$arg\""
+        fi
+        i=$((i+1))
+    done
+    case $i in
+        (0) set -- ;;
+        (1) set -- "$args0" ;;
+        (2) set -- "$args0" "$args1" ;;
+        (3) set -- "$args0" "$args1" "$args2" ;;
+        (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+        (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+        (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+        (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+        (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+        (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+    esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+    JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [[ "$(uname)" == "Darwin" ]] && [[ "$HOME" == "$PWD" ]]; then
+  cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/gradlew.bat b/gradlew.bat
new file mode 100644 (file)
index 0000000..e95643d
--- /dev/null
@@ -0,0 +1,84 @@
+@if "%DEBUG%" == "" @echo off\r
+@rem ##########################################################################\r
+@rem\r
+@rem  Gradle startup script for Windows\r
+@rem\r
+@rem ##########################################################################\r
+\r
+@rem Set local scope for the variables with windows NT shell\r
+if "%OS%"=="Windows_NT" setlocal\r
+\r
+set DIRNAME=%~dp0\r
+if "%DIRNAME%" == "" set DIRNAME=.\r
+set APP_BASE_NAME=%~n0\r
+set APP_HOME=%DIRNAME%\r
+\r
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\r
+set DEFAULT_JVM_OPTS=\r
+\r
+@rem Find java.exe\r
+if defined JAVA_HOME goto findJavaFromJavaHome\r
+\r
+set JAVA_EXE=java.exe\r
+%JAVA_EXE% -version >NUL 2>&1\r
+if "%ERRORLEVEL%" == "0" goto init\r
+\r
+echo.\r
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\r
+echo.\r
+echo Please set the JAVA_HOME variable in your environment to match the\r
+echo location of your Java installation.\r
+\r
+goto fail\r
+\r
+:findJavaFromJavaHome\r
+set JAVA_HOME=%JAVA_HOME:"=%\r
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe\r
+\r
+if exist "%JAVA_EXE%" goto init\r
+\r
+echo.\r
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\r
+echo.\r
+echo Please set the JAVA_HOME variable in your environment to match the\r
+echo location of your Java installation.\r
+\r
+goto fail\r
+\r
+:init\r
+@rem Get command-line arguments, handling Windows variants\r
+\r
+if not "%OS%" == "Windows_NT" goto win9xME_args\r
+\r
+:win9xME_args\r
+@rem Slurp the command line arguments.\r
+set CMD_LINE_ARGS=\r
+set _SKIP=2\r
+\r
+:win9xME_args_slurp\r
+if "x%~1" == "x" goto execute\r
+\r
+set CMD_LINE_ARGS=%*\r
+\r
+:execute\r
+@rem Setup the command line\r
+\r
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar\r
+\r
+@rem Execute Gradle\r
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\r
+\r
+:end\r
+@rem End local scope for the variables with windows NT shell\r
+if "%ERRORLEVEL%"=="0" goto mainEnd\r
+\r
+:fail\r
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\r
+rem the _cmd.exe /c_ return code!\r
+if  not "" == "%GRADLE_EXIT_CONSOLE%" exit 1\r
+exit /b 1\r
+\r
+:mainEnd\r
+if "%OS%"=="Windows_NT" endlocal\r
+\r
+:omega\r
diff --git a/pom.xml b/pom.xml
deleted file mode 100644 (file)
index 1799801..0000000
--- a/pom.xml
+++ /dev/null
@@ -1,212 +0,0 @@
-<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
-       <modelVersion>4.0.0</modelVersion>
-       <groupId>net.pterodactylus</groupId>
-       <artifactId>sone</artifactId>
-       <version>0.9.5</version>
-       <dependencies>
-               <dependency>
-                       <groupId>net.pterodactylus</groupId>
-                       <artifactId>utils</artifactId>
-                       <version>${version.utils}</version>
-               </dependency>
-               <dependency>
-                       <groupId>junit</groupId>
-                       <artifactId>junit</artifactId>
-                       <version>4.11</version>
-                       <scope>test</scope>
-               </dependency>
-               <dependency>
-                       <groupId>org.mockito</groupId>
-                       <artifactId>mockito-all</artifactId>
-                       <version>1.9.5</version>
-                       <scope>test</scope>
-               </dependency>
-               <dependency>
-                       <groupId>org.hamcrest</groupId>
-                       <artifactId>hamcrest-all</artifactId>
-                       <version>1.3</version>
-               </dependency>
-               <dependency>
-                       <groupId>org.jsoup</groupId>
-                       <artifactId>jsoup</artifactId>
-                       <version>1.7.1</version>
-                       <scope>test</scope>
-               </dependency>
-               <dependency>
-                       <groupId>org.freenetproject</groupId>
-                       <artifactId>fred</artifactId>
-                       <version>0.7.5.1467.99.3</version>
-                       <scope>provided</scope>
-               </dependency>
-               <dependency>
-                       <groupId>org.freenetproject</groupId>
-                       <artifactId>freenet-ext</artifactId>
-                       <version>29</version>
-                       <scope>provided</scope>
-               </dependency>
-               <dependency>
-                       <groupId>com.google.inject</groupId>
-                       <artifactId>guice</artifactId>
-                       <version>3.0</version>
-               </dependency>
-               <dependency>
-                       <groupId>com.google.guava</groupId>
-                       <artifactId>guava</artifactId>
-                       <version>14.0.1</version>
-               </dependency>
-               <dependency>
-                       <groupId>commons-lang</groupId>
-                       <artifactId>commons-lang</artifactId>
-                       <version>2.6</version>
-               </dependency>
-               <dependency>
-                       <groupId>com.fasterxml.jackson.core</groupId>
-                       <artifactId>jackson-databind</artifactId>
-                       <version>2.1.2</version>
-               </dependency>
-               <dependency>
-                       <groupId>com.google.code.findbugs</groupId>
-                       <artifactId>jsr305</artifactId>
-                       <version>2.0.1</version>
-               </dependency>
-       </dependencies>
-       <repositories>
-               <repository>
-                       <id>pterodactylus</id>
-                       <url>http://maven.pterodactylus.net/</url>
-               </repository>
-       </repositories>
-       <properties>
-               <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
-               <version.utils>0.12.4</version.utils>
-               <findbugs.timeout>600000</findbugs.timeout>
-       </properties>
-       <build>
-               <plugins>
-                       <plugin>
-                               <groupId>org.codehaus.mojo</groupId>
-                               <artifactId>findbugs-maven-plugin</artifactId>
-                               <version>2.5.2</version>
-                               <configuration>
-                                       <timeout>${findbugs.timeout}</timeout>
-                               </configuration>
-                       </plugin>
-                       <plugin>
-                               <groupId>org.apache.maven.plugins</groupId>
-                               <artifactId>maven-compiler-plugin</artifactId>
-                               <version>2.0.2</version>
-                               <configuration>
-                                       <source>1.6</source>
-                                       <target>1.6</target>
-                                       <encoding>UTF-8</encoding>
-                               </configuration>
-                       </plugin>
-                       <plugin>
-                               <groupId>org.apache.maven.plugins</groupId>
-                               <artifactId>maven-jar-plugin</artifactId>
-                               <version>2.2</version>
-                               <configuration>
-                                       <archive>
-                                               <manifestEntries>
-                                                       <Plugin-Main-Class>net.pterodactylus.sone.main.SonePlugin</Plugin-Main-Class>
-                                               </manifestEntries>
-                                       </archive>
-                               </configuration>
-                       </plugin>
-                       <plugin>
-                               <groupId>org.apache.maven.plugins</groupId>
-                               <artifactId>maven-assembly-plugin</artifactId>
-                               <version>2.2-beta-5</version>
-                               <configuration>
-                                       <finalName>sone</finalName>
-                                       <descriptorRefs>
-                                               <descriptorRef>jar-with-dependencies</descriptorRef>
-                                       </descriptorRefs>
-                                       <archiverConfig>
-                                               <duplicateBehavior>skip</duplicateBehavior>
-                                       </archiverConfig>
-                                       <archive>
-                                               <manifestEntries>
-                                                       <Plugin-Main-Class>net.pterodactylus.sone.main.SonePlugin</Plugin-Main-Class>
-                                               </manifestEntries>
-                                       </archive>
-                               </configuration>
-                               <executions>
-                                       <execution>
-                                               <id>make-assembly</id>
-                                               <phase>package</phase>
-                                               <goals>
-                                                       <goal>single</goal>
-                                               </goals>
-                                       </execution>
-                               </executions>
-                       </plugin>
-                       <plugin>
-                               <groupId>org.apache.maven.plugins</groupId>
-                               <artifactId>maven-javadoc-plugin</artifactId>
-                               <version>2.7</version>
-                               <configuration>
-                                       <detectLinks>true</detectLinks>
-                                       <detectJavaApiLink>true</detectJavaApiLink>
-                                       <show>private</show>
-                                       <footer>© 2010–2013 David ‘Bombe’ Roden</footer>
-                               </configuration>
-                       </plugin>
-                       <plugin>
-                               <groupId>org.jacoco</groupId>
-                               <artifactId>jacoco-maven-plugin</artifactId>
-                               <version>0.7.6.201602180812</version>
-                               <executions>
-                                       <execution>
-                                               <id>default-prepare-agent</id>
-                                               <goals>
-                                                       <goal>prepare-agent</goal>
-                                               </goals>
-                                       </execution>
-                                       <execution>
-                                               <id>default-report</id>
-                                               <phase>prepare-package</phase>
-                                               <goals>
-                                                       <goal>report</goal>
-                                               </goals>
-                                       </execution>
-                                       <execution>
-                                               <id>default-check</id>
-                                               <goals>
-                                                       <goal>check</goal>
-                                               </goals>
-                                               <configuration>
-                                                       <rules>
-                                                               <!--  implmentation is needed only for Maven 2  -->
-                                                               <rule implementation="org.jacoco.maven.RuleConfiguration">
-                                                                       <element>BUNDLE</element>
-                                                                       <limits>
-                                                                               <!--  implmentation is needed only for Maven 2  -->
-                                                                               <limit implementation="org.jacoco.report.check.Limit">
-                                                                                       <counter>COMPLEXITY</counter>
-                                                                                       <value>COVEREDRATIO</value>
-                                                                                       <minimum>0.60</minimum>
-                                                                               </limit>
-                                                                       </limits>
-                                                               </rule>
-                                                       </rules>
-                                               </configuration>
-                                       </execution>
-                               </executions>
-                       </plugin>
-                       <plugin>
-                               <groupId>org.pitest</groupId>
-                               <artifactId>pitest-maven</artifactId>
-                               <version>1.1.10</version>
-                               <configuration>
-                                       <targetClasses>
-                                               <param>net.pterodactylus.sone.*</param>
-                                       </targetClasses>
-                                       <targetTests>
-                                               <param>net.pterodactylus.sone.*</param>
-                                       </targetTests>
-                               </configuration>
-                       </plugin>
-               </plugins>
-       </build>
-</project>
diff --git a/settings.gradle b/settings.gradle
new file mode 100644 (file)
index 0000000..e60d974
--- /dev/null
@@ -0,0 +1 @@
+rootProject.name = 'sone'
index 3b958f1..91825a6 100644 (file)
@@ -643,7 +643,7 @@ public class Core extends AbstractService implements SoneProvider, PostProvider,
                Sone sone = database.newSoneBuilder().local().from(ownIdentity).build();
                String property = fromNullable(ownIdentity.getProperty("Sone.LatestEdition")).or("0");
                sone.setLatestEdition(fromNullable(tryParse(property)).or(0L));
-               sone.setClient(new Client("Sone", SonePlugin.VERSION.toString()));
+               sone.setClient(new Client("Sone", SonePlugin.getPluginVersion()));
                sone.setKnown(true);
                SoneInserter soneInserter = new SoneInserter(this, eventBus, freenetInterface, ownIdentity.getId());
                eventBus.register(soneInserter);
index 46558ab..26135ea 100644 (file)
@@ -307,7 +307,7 @@ public class SoneInserter extends AbstractService {
                        soneProperties.put("time", currentTimeMillis());
                        soneProperties.put("requestUri", sone.getRequestUri());
                        soneProperties.put("profile", sone.getProfile());
-                       soneProperties.put("posts", Ordering.from(Post.TIME_COMPARATOR).sortedCopy(sone.getPosts()));
+                       soneProperties.put("posts", Ordering.from(Post.NEWEST_FIRST).sortedCopy(sone.getPosts()));
                        soneProperties.put("replies", Ordering.from(Reply.TIME_COMPARATOR).reverse().sortedCopy(sone.getReplies()));
                        soneProperties.put("likedPostIds", new HashSet<String>(sone.getLikedPostIds()));
                        soneProperties.put("likedReplyIds", new HashSet<String>(sone.getLikedReplyIds()));
@@ -393,7 +393,7 @@ public class SoneInserter extends AbstractService {
                        templateContext.set("core", core);
                        templateContext.set("currentSone", soneProperties);
                        templateContext.set("currentEdition", core.getUpdateChecker().getLatestEdition());
-                       templateContext.set("version", SonePlugin.VERSION);
+                       templateContext.set("version", SonePlugin.getPluginVersion());
                        StringWriter writer = new StringWriter();
                        try {
                                template.render(templateContext, writer);
index 990e20d..5a1c2c5 100644 (file)
@@ -33,7 +33,7 @@ import com.google.common.base.Predicate;
 public interface Post extends Identified {
 
        /** Comparator for posts, sorts descending by time. */
-       public static final Comparator<Post> TIME_COMPARATOR = new Comparator<Post>() {
+       public static final Comparator<Post> NEWEST_FIRST = new Comparator<Post>() {
 
                @Override
                public int compare(Post leftPost, Post rightPost) {
index f33a446..9e2c50a 100644 (file)
@@ -19,8 +19,6 @@ package net.pterodactylus.sone.data.impl;
 
 import static com.google.common.base.Preconditions.checkState;
 
-import org.apache.commons.lang.StringUtils;
-
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.database.PostBuilder;
 import net.pterodactylus.sone.database.SoneProvider;
@@ -163,7 +161,7 @@ public abstract class AbstractPostBuilder implements PostBuilder {
                checkState((randomId && (id == null)) || (!randomId && (id != null)), "exactly one of random ID or custom ID must be set");
                checkState(senderId != null, "sender must not be null");
                checkState((currentTime && (time == 0)) || (!currentTime && (time > 0)), "one of current time or custom time must be set");
-               checkState(!StringUtils.isBlank(text), "text must not be empty");
+               checkState((text != null) && !text.trim().isEmpty(), "text must not be empty");
                checkState((recipientId == null) || !recipientId.equals(senderId), "sender and recipient must not be the same");
        }
 
index 10bdf76..bd8090f 100644 (file)
@@ -19,8 +19,6 @@ package net.pterodactylus.sone.data.impl;
 
 import static com.google.common.base.Preconditions.checkState;
 
-import org.apache.commons.lang.StringUtils;
-
 import net.pterodactylus.sone.database.PostReplyBuilder;
 
 /**
@@ -58,7 +56,7 @@ public abstract class AbstractPostReplyBuilder extends AbstractReplyBuilder<Post
                checkState((randomId && (id == null)) || (!randomId && (id != null)), "either random ID nor custom ID must be set");
                checkState(senderId != null, "sender must not be null");
                checkState((currentTime && (time == 0)) || (!currentTime && (time >= 0)), "either current time or custom time must be set");
-               checkState(!StringUtils.isBlank(text), "text must not be empty");
+               checkState((text != null) && !text.trim().isEmpty(), "text must not be empty");
                checkState(postId != null, "post must not be null");
        }
 
index 30f32fc..7235bc9 100644 (file)
@@ -26,8 +26,6 @@ import net.pterodactylus.sone.database.PostProvider;
 import net.pterodactylus.sone.database.PostReplyBuilder;
 import net.pterodactylus.sone.database.SoneProvider;
 
-import org.apache.commons.lang.StringUtils;
-
 /**
  * {@link PostReplyBuilder} implementation that creates {@link PostReplyImpl}
  * objects.
@@ -63,7 +61,7 @@ public class PostReplyBuilderImpl extends AbstractPostReplyBuilder {
                checkState((randomId && (id == null)) || (!randomId && (id != null)), "either random ID nor custom ID must be set");
                checkState(senderId != null, "sender must not be null");
                checkState((currentTime && (time == 0)) || (!currentTime && (time >= 0)), "either current time or custom time must be set");
-               checkState(!StringUtils.isBlank(text), "text must not be empty");
+               checkState((text != null) && !text.trim().isEmpty(), "text must not be empty");
                checkState(postId != null, "post must not be null");
 
                /* create new post reply. */
index 8a6dd80..04a5bcc 100644 (file)
@@ -368,7 +368,7 @@ public class SoneImpl implements Sone {
                synchronized (this) {
                        sortedPosts = new ArrayList<Post>(posts);
                }
-               Collections.sort(sortedPosts, Post.TIME_COMPARATOR);
+               Collections.sort(sortedPosts, Post.NEWEST_FIRST);
                return sortedPosts;
        }
 
index bbff5ed..dea5156 100644 (file)
@@ -74,7 +74,7 @@ public class GetPostFeedCommand extends AbstractSoneCommand {
                allPosts = Collections2.filter(allPosts, Post.FUTURE_POSTS_FILTER);
 
                List<Post> sortedPosts = new ArrayList<Post>(allPosts);
-               Collections.sort(sortedPosts, Post.TIME_COMPARATOR);
+               Collections.sort(sortedPosts, Post.NEWEST_FIRST);
 
                if (sortedPosts.size() < startPost) {
                        return new Response("PostFeed", encodePosts(Collections.<Post> emptyList(), "Posts.", false));
index b887926..ee0f2e5 100644 (file)
@@ -45,7 +45,7 @@ public class VersionCommand extends AbstractSoneCommand {
         */
        @Override
        public Response execute(SimpleFieldSet parameters, Bucket data, AccessType accessType) {
-               return new Response("Version", new SimpleFieldSetBuilder().put("Version", SonePlugin.VERSION.toString()).put("ProtocolVersion", 1).get());
+               return new Response("Version", new SimpleFieldSetBuilder().put("Version", SonePlugin.getPluginVersion()).put("ProtocolVersion", 1).get());
        }
 
 }
index bff0a49..61bd6ce 100644 (file)
@@ -116,12 +116,12 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr
        }
 
        /** The version. */
-       public static final Version VERSION = new Version(0, 9, 5);
+       private static final Version VERSION = new Version(0, 9, 6);
 
        /** The current year at time of release. */
        private static final int YEAR = 2016;
        private static final String SONE_HOMEPAGE = "USK@nwa8lHa271k2QvJ8aa0Ov7IHAV-DFOCFgmDt3X6BpCI,DuQSUZiI~agF8c-6tjsFFGuZ8eICrzWCILB60nT8KKo,AQACAAE/sone/";
-       private static final int LATEST_EDITION = 72;
+       private static final int LATEST_EDITION = 73;
 
        /** The logger. */
        private static final Logger logger = getLogger(SonePlugin.class.getName());
@@ -175,6 +175,10 @@ public class SonePlugin implements FredPlugin, FredPluginFCP, FredPluginL10n, Fr
                return l10n;
        }
 
+       public static String getPluginVersion() {
+               return VERSION.toString();
+       }
+
        public static int getYear() {
                return YEAR;
        }
index 2f1526f..511baf8 100644 (file)
@@ -45,7 +45,7 @@ import com.google.common.base.Optional;
 public class ImageLinkFilter implements Filter {
 
        /** The template to render for the &lt;img&gt; tag. */
-       private static final Template linkTemplate = TemplateParser.parse(new StringReader("<img<%ifnull !class> class=\"<%class|css>\"<%/if> src=\"<%src|html><%if forceDownload>?forcedownload=true<%/if>\" alt=\"<%alt|html>\" title=\"<%title|html>\" width=\"<%width|html>\" height=\"<%height|html>\" style=\"position: relative;<%ifnull ! top>top: <% top|html>;<%/if><%ifnull ! left>left: <% left|html>;<%/if>\"/>"));
+       private static final Template linkTemplate = TemplateParser.parse(new StringReader("<img<%ifnull !class> class=\"<%class|css>\"<%/if> src=\"<%src|html>\" alt=\"<%alt|html>\" title=\"<%title|html>\" width=\"<%width|html>\" height=\"<%height|html>\" style=\"position: relative;<%ifnull ! top>top: <% top|html>;<%/if><%ifnull ! left>left: <% left|html>;<%/if>\"/>"));
 
        /** The core. */
        private final Core core;
@@ -90,7 +90,6 @@ public class ImageLinkFilter implements Filter {
                linkTemplateContext.set("class", imageClass);
                if (image.isInserted()) {
                        linkTemplateContext.set("src", "/" + image.getKey());
-                       linkTemplateContext.set("forceDownload", true);
                } else {
                        linkTemplateContext.set("src", "getImage.html?image=" + image.getId());
                }
index 479b3b2..7cf8c70 100644 (file)
@@ -39,7 +39,6 @@ import net.pterodactylus.sone.text.PostPart;
 import net.pterodactylus.sone.text.SonePart;
 import net.pterodactylus.sone.text.SoneTextParser;
 import net.pterodactylus.sone.text.SoneTextParserContext;
-import net.pterodactylus.sone.web.page.FreenetRequest;
 import net.pterodactylus.util.template.Filter;
 import net.pterodactylus.util.template.Template;
 import net.pterodactylus.util.template.TemplateContext;
@@ -97,8 +96,7 @@ public class ParserFilter implements Filter {
                if (sone instanceof String) {
                        sone = core.getSone((String) sone).orNull();
                }
-               FreenetRequest request = (FreenetRequest) templateContext.get("request");
-               SoneTextParserContext context = new SoneTextParserContext(request, (Sone) sone);
+               SoneTextParserContext context = new SoneTextParserContext((Sone) sone);
                StringWriter parsedTextWriter = new StringWriter();
                Iterable<Part> parts = soneTextParser.parse(text, context);
                if (length > -1) {
@@ -246,7 +244,7 @@ public class ParserFilter implements Filter {
         */
        private void render(Writer writer, PostPart postPart) {
                SoneTextParser parser = new SoneTextParser(core, core);
-               SoneTextParserContext parserContext = new SoneTextParserContext(null, postPart.getPost().getSone());
+               SoneTextParserContext parserContext = new SoneTextParserContext(postPart.getPost().getSone());
                Iterable<Part> parts = parser.parse(postPart.getPost().getText(), parserContext);
                StringBuilder excerpt = new StringBuilder();
                for (Part part : parts) {
index 829bf0f..493880f 100644 (file)
@@ -17,6 +17,8 @@
 
 package net.pterodactylus.sone.text;
 
+import javax.annotation.Nonnull;
+
 /**
  * {@link LinkPart} implementation that stores an additional attribute: if the
  * link is an SSK or USK link and the post was created by an identity that owns
@@ -26,49 +28,17 @@ package net.pterodactylus.sone.text;
  */
 public class FreenetLinkPart extends LinkPart {
 
-       /** Whether the link is trusted. */
        private final boolean trusted;
 
-       /**
-        * Creates a new freenet link part.
-        *
-        * @param link
-        *            The link of the part
-        * @param text
-        *            The text of the part
-        * @param trusted
-        *            {@code true} if the link is trusted, {@code false} otherwise
-        */
-       public FreenetLinkPart(String link, String text, boolean trusted) {
+       public FreenetLinkPart(@Nonnull String link, @Nonnull String text, boolean trusted) {
                this(link, text, text, trusted);
        }
 
-       /**
-        * Creates a new freenet link part.
-        *
-        * @param link
-        *            The link of the part
-        * @param text
-        *            The text of the part
-        * @param title
-        *            The title of the part
-        * @param trusted
-        *            {@code true} if the link is trusted, {@code false} otherwise
-        */
-       public FreenetLinkPart(String link, String text, String title, boolean trusted) {
+       public FreenetLinkPart(@Nonnull String link, @Nonnull String text, @Nonnull String title, boolean trusted) {
                super(link, text, title);
                this.trusted = trusted;
        }
 
-       //
-       // ACCESSORS
-       //
-
-       /**
-        * Returns whether the link is trusted.
-        *
-        * @return {@code true} if the link is trusted, {@code false} otherwise
-        */
        public boolean isTrusted() {
                return trusted;
        }
index 6764328..9f889ef 100644 (file)
 
 package net.pterodactylus.sone.text;
 
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+
 /**
  * {@link Part} implementation that can hold a link. A link contains of three
  * attributes: the link itself, the text that is shown instead of the link, and
@@ -26,75 +30,32 @@ package net.pterodactylus.sone.text;
  */
 public class LinkPart implements Part {
 
-       /** The link of this part. */
        private final String link;
-
-       /** The text of this part. */
        private final String text;
-
-       /** The title of this part. */
        private final String title;
 
-       /**
-        * Creates a new link part.
-        *
-        * @param link
-        *            The link of the link part
-        * @param text
-        *            The text of the link part
-        */
-       public LinkPart(String link, String text) {
+       public LinkPart(@Nonnull String link, @Nonnull String text) {
                this(link, text, text);
        }
 
-       /**
-        * Creates a new link part.
-        *
-        * @param link
-        *            The link of the link part
-        * @param text
-        *            The text of the link part
-        * @param title
-        *            The title of the link part
-        */
-       public LinkPart(String link, String text, String title) {
-               this.link = link;
-               this.text = text;
-               this.title = title;
+       public LinkPart(@Nonnull String link, @Nonnull String text, @Nonnull String title) {
+               this.link = Objects.requireNonNull(link);
+               this.text = Objects.requireNonNull(text);
+               this.title = Objects.requireNonNull(title);
        }
 
-       //
-       // ACCESSORS
-       //
-
-       /**
-        * Returns the link of this part.
-        *
-        * @return The link of this part
-        */
+       @Nonnull
        public String getLink() {
                return link;
        }
 
-       /**
-        * Returns the title of this part.
-        *
-        * @return The title of this part
-        */
+       @Nonnull
        public String getTitle() {
                return title;
        }
 
-       //
-       // PART METHODS
-       //
-
-       /**
-        * Returns the text of this part.
-        *
-        * @return The text of this part
-        */
        @Override
+       @Nonnull
        public String getText() {
                return text;
        }
index d1c89b3..3444d19 100644 (file)
@@ -23,6 +23,9 @@ import java.util.Deque;
 import java.util.Iterator;
 import java.util.List;
 import java.util.NoSuchElementException;
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
 
 /**
  * Part implementation that can contain an arbitrary amount of other parts.
@@ -33,61 +36,27 @@ import java.util.NoSuchElementException;
  */
 public class PartContainer implements Part, Iterable<Part> {
 
-       /** The parts to render. */
        private final List<Part> parts = new ArrayList<Part>();
 
-       //
-       // ACCESSORS
-       //
-
-       /**
-        * Adds a part to render.
-        *
-        * @param part
-        *            The part to add
-        */
-       public void add(Part part) {
-               parts.add(part);
+       public void add(@Nonnull Part part) {
+               parts.add(Objects.requireNonNull(part));
        }
 
-       /**
-        * Returns the part at the given index.
-        *
-        * @param index
-        *            The index of the part
-        * @return The part
-        */
+       @Nonnull
        public Part getPart(int index) {
                return parts.get(index);
        }
 
-       /**
-        * Removes the part at the given index.
-        *
-        * @param index
-        *            The index of the part to remove
-        */
        public void removePart(int index) {
                parts.remove(index);
        }
 
-       /**
-        * Returns the number of parts.
-        *
-        * @return The number of parts
-        */
        public int size() {
                return parts.size();
        }
 
-       //
-       // PART METHODS
-       //
-
-       /**
-        * {@inheritDoc}
-        */
        @Override
+       @Nonnull
        public String getText() {
                StringBuilder partText = new StringBuilder();
                for (Part part : parts) {
@@ -96,14 +65,8 @@ public class PartContainer implements Part, Iterable<Part> {
                return partText.toString();
        }
 
-       //
-       // ITERABLE METHODS
-       //
-
-       /**
-        * {@inheritDoc}
-        */
        @Override
+       @Nonnull
        @SuppressWarnings("synthetic-access")
        public Iterator<Part> iterator() {
                return new Iterator<Part>() {
index 4f15a73..7bdbee9 100644 (file)
 
 package net.pterodactylus.sone.text;
 
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+
 /**
  * {@link Part} implementation that holds a single piece of text.
  *
@@ -24,29 +28,14 @@ package net.pterodactylus.sone.text;
  */
 public class PlainTextPart implements Part {
 
-       /** The text of the part. */
        private final String text;
 
-       /**
-        * Creates a new plain-text part.
-        *
-        * @param text
-        *            The text of the part
-        */
-       public PlainTextPart(String text) {
-               this.text = text;
+       public PlainTextPart(@Nonnull String text) {
+               this.text = Objects.requireNonNull(text);
        }
 
-       //
-       // PART METHODS
-       //
-
-       /**
-        * Returns the text of this part.
-        *
-        * @return The text of this part
-        */
        @Override
+       @Nonnull
        public String getText() {
                return text;
        }
index 68075a5..e70448f 100644 (file)
 
 package net.pterodactylus.sone.text;
 
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+
 import net.pterodactylus.sone.data.Post;
 
 /**
@@ -26,39 +30,17 @@ import net.pterodactylus.sone.data.Post;
  */
 public class PostPart implements Part {
 
-       /** The post this part refers to. */
        private final Post post;
 
-       /**
-        * Creates a new post part.
-        *
-        * @param post
-        *            The referenced post
-        */
-       public PostPart(Post post) {
-               this.post = post;
+       public PostPart(@Nonnull Post post) {
+               this.post = Objects.requireNonNull(post);
        }
 
-       //
-       // ACCESSORS
-       //
-
-       /**
-        * Returns the post referenced by this part.
-        *
-        * @return The post referenced by this part
-        */
+       @Nonnull
        public Post getPost() {
                return post;
        }
 
-       //
-       // PART METHODS
-       //
-
-       /**
-        * {@inheritDoc}
-        */
        @Override
        public String getText() {
                return post.getText();
index cbafc61..576eb7b 100644 (file)
 
 package net.pterodactylus.sone.text;
 
+import java.util.Objects;
+
+import javax.annotation.Nonnull;
+
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.template.SoneAccessor;
 
@@ -27,39 +31,17 @@ import net.pterodactylus.sone.template.SoneAccessor;
  */
 public class SonePart implements Part {
 
-       /** The referenced {@link Sone}. */
        private final Sone sone;
 
-       /**
-        * Creates a new Sone part.
-        *
-        * @param sone
-        *            The referenced Sone
-        */
-       public SonePart(Sone sone) {
-               this.sone = sone;
+       public SonePart(@Nonnull Sone sone) {
+               this.sone = Objects.requireNonNull(sone);
        }
 
-       //
-       // ACCESSORS
-       //
-
-       /**
-        * Returns the referenced Sone.
-        *
-        * @return The referenced Sone
-        */
+       @Nonnull
        public Sone getSone() {
                return sone;
        }
 
-       //
-       // PART METHODS
-       //
-
-       /**
-        * {@inheritDoc}
-        */
        @Override
        public String getText() {
                return SoneAccessor.getNiceName(sone);
index fb4e7cd..81ac751 100644 (file)
@@ -21,6 +21,7 @@ import static java.util.logging.Logger.getLogger;
 
 import java.io.BufferedReader;
 import java.io.IOException;
+import java.io.Reader;
 import java.io.StringReader;
 import java.net.MalformedURLException;
 import java.util.logging.Level;
@@ -36,7 +37,6 @@ import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.data.impl.IdOnlySone;
 import net.pterodactylus.sone.database.PostProvider;
 import net.pterodactylus.sone.database.SoneProvider;
-import net.pterodactylus.util.io.Closer;
 
 import com.google.common.base.Optional;
 
@@ -124,8 +124,8 @@ public class SoneTextParser implements Parser<SoneTextParserContext> {
        @Override
        public Iterable<Part> parse(@Nonnull String source, @Nullable SoneTextParserContext context) {
                PartContainer parts = new PartContainer();
-               BufferedReader bufferedReader = new BufferedReader(new StringReader(source));
-               try {
+               try (Reader sourceReader = new StringReader(source);
+                               BufferedReader bufferedReader = new BufferedReader(sourceReader)) {
                        String line;
                        boolean lastLineEmpty = true;
                        int emptyLines = 0;
@@ -175,10 +175,8 @@ public class SoneTextParser implements Parser<SoneTextParserContext> {
                                        }
                                        lineComplete = false;
 
-                                       Matcher matcher = whitespacePattern.matcher(line);
-                                       int nextSpace = matcher.find(0) ? matcher.start() : line.length();
-                                       String link = line.substring(0, nextSpace);
-                                       String name = link;
+                                       int endOfLink = findEndOfLink(line);
+                                       String link = line.substring(0, endOfLink);
                                        logger.log(Level.FINER, String.format("Found link: %s", link));
 
                                        /* if there is no text after the scheme, it’s not a link! */
@@ -188,98 +186,32 @@ public class SoneTextParser implements Parser<SoneTextParserContext> {
                                                continue;
                                        }
 
-                                       if (linkType == LinkType.SONE) {
-                                               if (line.length() >= (7 + 43)) {
-                                                       String soneId = line.substring(7, 50);
-                                                       Optional<Sone> sone = soneProvider.getSone(soneId);
-                                                       if (!sone.isPresent()) {
-                                                               /*
-                                                                * don’t use create=true above, we don’t want
-                                                                * the empty shell.
-                                                                */
-                                                               sone = Optional.<Sone>of(new IdOnlySone(soneId));
-                                                       }
-                                                       parts.add(new SonePart(sone.get()));
-                                                       line = line.substring(50);
-                                               } else {
-                                                       parts.add(new PlainTextPart(line));
-                                                       line = "";
-                                               }
-                                               continue;
-                                       }
-                                       if (linkType == LinkType.POST) {
-                                               if (line.length() >= (7 + 36)) {
-                                                       String postId = line.substring(7, 43);
-                                                       Optional<Post> post = postProvider.getPost(postId);
-                                                       if (post.isPresent()) {
-                                                               parts.add(new PostPart(post.get()));
-                                                       } else {
-                                                               parts.add(new PlainTextPart(line.substring(0, 43)));
-                                                       }
-                                                       line = line.substring(43);
-                                               } else {
-                                                       parts.add(new PlainTextPart(line));
-                                                       line = "";
-                                               }
-                                               continue;
+                                       switch (linkType) {
+                                               case SONE:
+                                                       renderSoneLink(parts, link);
+                                                       break;
+                                               case POST:
+                                                       renderPostLink(parts, link);
+                                                       break;
+                                               case KSK:
+                                               case CHK:
+                                               case SSK:
+                                               case USK:
+                                                       renderFreenetLink(parts, link, linkType, context);
+                                                       break;
+                                               case HTTP:
+                                               case HTTPS:
+                                                       renderHttpLink(parts, link, linkType);
+                                                       break;
                                        }
 
-                                       if (linkType.isFreenetLink()) {
-                                               FreenetURI uri;
-                                               if (name.indexOf('?') > -1) {
-                                                       name = name.substring(0, name.indexOf('?'));
-                                               }
-                                               if (name.endsWith("/")) {
-                                                       name = name.substring(0, name.length() - 1);
-                                               }
-                                               try {
-                                                       uri = new FreenetURI(name);
-                                                       name = uri.lastMetaString();
-                                                       if (name == null) {
-                                                               name = uri.getDocName();
-                                                       }
-                                                       if (name == null) {
-                                                               name = link.substring(0, Math.min(9, link.length()));
-                                                       }
-                                                       boolean fromPostingSone = ((linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (context != null) && (context.getPostingSone() != null) && link.substring(4, Math.min(link.length(), 47)).equals(context.getPostingSone().getId());
-                                                       parts.add(new FreenetLinkPart(link, name, fromPostingSone));
-                                               } catch (MalformedURLException mue1) {
-                                                       /* not a valid link, insert as plain text. */
-                                                       parts.add(new PlainTextPart(link));
-                                               } catch (NullPointerException npe1) {
-                                                       /* FreenetURI sometimes throws these, too. */
-                                                       parts.add(new PlainTextPart(link));
-                                               } catch (ArrayIndexOutOfBoundsException aioobe1) {
-                                                       /* oh, and these, too. */
-                                                       parts.add(new PlainTextPart(link));
-                                               }
-                                       } else if ((linkType == LinkType.HTTP) || (linkType == LinkType.HTTPS)) {
-                                               name = link.substring(linkType == LinkType.HTTP ? 7 : 8);
-                                               int firstSlash = name.indexOf('/');
-                                               int lastSlash = name.lastIndexOf('/');
-                                               if ((lastSlash - firstSlash) > 3) {
-                                                       name = name.substring(0, firstSlash + 1) + "…" + name.substring(lastSlash);
-                                               }
-                                               if (name.endsWith("/")) {
-                                                       name = name.substring(0, name.length() - 1);
-                                               }
-                                               if (((name.indexOf('/') > -1) && (name.indexOf('.') < name.lastIndexOf('.', name.indexOf('/'))) || ((name.indexOf('/') == -1) && (name.indexOf('.') < name.lastIndexOf('.')))) && name.startsWith("www.")) {
-                                                       name = name.substring(4);
-                                               }
-                                               if (name.indexOf('?') > -1) {
-                                                       name = name.substring(0, name.indexOf('?'));
-                                               }
-                                               parts.add(new LinkPart(link, name));
-                                       }
-                                       line = line.substring(nextSpace);
+                                       line = line.substring(endOfLink);
                                }
                                lastLineEmpty = false;
                        }
                } catch (IOException ioe1) {
                        // a buffered reader around a string reader should never throw.
                        throw new RuntimeException(ioe1);
-               } finally {
-                       Closer.close(bufferedReader);
                }
                for (int partIndex = parts.size() - 1; partIndex >= 0; --partIndex) {
                        Part part = parts.getPart(partIndex);
@@ -291,6 +223,108 @@ public class SoneTextParser implements Parser<SoneTextParserContext> {
                return parts;
        }
 
+       private void renderSoneLink(PartContainer parts, String line) {
+               if (line.length() >= (7 + 43)) {
+                       String soneId = line.substring(7, 50);
+                       Optional<Sone> sone = soneProvider.getSone(soneId);
+                       parts.add(new SonePart(sone.or(new IdOnlySone(soneId))));
+               } else {
+                       parts.add(new PlainTextPart(line));
+               }
+       }
+
+       private void renderPostLink(PartContainer parts, String line) {
+               if (line.length() >= (7 + 36)) {
+                       String postId = line.substring(7, 43);
+                       Optional<Post> post = postProvider.getPost(postId);
+                       if (post.isPresent()) {
+                               parts.add(new PostPart(post.get()));
+                       } else {
+                               parts.add(new PlainTextPart(line.substring(0, 43)));
+                       }
+               } else {
+                       parts.add(new PlainTextPart(line));
+               }
+       }
+
+       private void renderFreenetLink(PartContainer parts, String link, LinkType linkType, @Nullable SoneTextParserContext context) {
+               String name = link;
+               if (name.indexOf('?') > -1) {
+                       name = name.substring(0, name.indexOf('?'));
+               }
+               if (name.endsWith("/")) {
+                       name = name.substring(0, name.length() - 1);
+               }
+               try {
+                       FreenetURI uri = new FreenetURI(name);
+                       name = uri.lastMetaString();
+                       if (name == null) {
+                               name = uri.getDocName();
+                       }
+                       if (name == null) {
+                               name = link.substring(0, Math.min(9, link.length()));
+                       }
+                       boolean fromPostingSone = ((linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (context != null) && (context.getPostingSone() != null) && link.substring(4, Math.min(link.length(), 47)).equals(context.getPostingSone().getId());
+                       parts.add(new FreenetLinkPart(link, name, fromPostingSone));
+               } catch (MalformedURLException mue1) {
+                       /* not a valid link, insert as plain text. */
+                       parts.add(new PlainTextPart(link));
+               } catch (NullPointerException npe1) {
+                       /* FreenetURI sometimes throws these, too. */
+                       parts.add(new PlainTextPart(link));
+               } catch (ArrayIndexOutOfBoundsException aioobe1) {
+                       /* oh, and these, too. */
+                       parts.add(new PlainTextPart(link));
+               }
+       }
+
+       private void renderHttpLink(PartContainer parts, String link, LinkType linkType) {
+               String name;
+               name = link.substring(linkType == LinkType.HTTP ? 7 : 8);
+               int firstSlash = name.indexOf('/');
+               int lastSlash = name.lastIndexOf('/');
+               if ((lastSlash - firstSlash) > 3) {
+                       name = name.substring(0, firstSlash + 1) + "…" + name.substring(lastSlash);
+               }
+               if (name.endsWith("/")) {
+                       name = name.substring(0, name.length() - 1);
+               }
+               if (((name.indexOf('/') > -1) && (name.indexOf('.') < name.lastIndexOf('.', name.indexOf('/'))) || ((name.indexOf('/') == -1) && (name.indexOf('.') < name.lastIndexOf('.')))) && name.startsWith("www.")) {
+                       name = name.substring(4);
+               }
+               if (name.indexOf('?') > -1) {
+                       name = name.substring(0, name.indexOf('?'));
+               }
+               parts.add(new LinkPart(link, name));
+       }
+
+       private int findEndOfLink(String line) {
+               Matcher matcher = whitespacePattern.matcher(line);
+               int endOfLink = matcher.find() ? matcher.start() : line.length();
+               while ((endOfLink > 0) && isPunctuation(line.charAt(endOfLink - 1))) {
+                       endOfLink--;
+               }
+               int openParens = 0;
+               for (int i = 0; i < endOfLink; i++) {
+                       switch (line.charAt(i)) {
+                               case '(':
+                                       openParens++;
+                                       break;
+                               case ')':
+                                       openParens--;
+                                       if (openParens < 0) {
+                                               return i;
+                                       }
+                               default:
+                       }
+               }
+               return endOfLink;
+       }
+
+       private static boolean isPunctuation(char character) {
+               return (character == '.') || (character == ',');
+       }
+
        private static class NextLink {
 
                private final int position;
index 8918e40..fba81ab 100644 (file)
@@ -29,35 +29,20 @@ import net.pterodactylus.sone.web.page.FreenetRequest;
  */
 public class SoneTextParserContext implements ParserContext {
 
-       /** The request being processed. */
-       private final FreenetRequest request;
-
        /** The posting Sone. */
        private final Sone postingSone;
 
        /**
         * Creates a new link parser context.
         *
-        * @param request
-        *            The request being processed
         * @param postingSone
         *            The posting Sone
         */
-       public SoneTextParserContext(FreenetRequest request, Sone postingSone) {
-               this.request = request;
+       public SoneTextParserContext(Sone postingSone) {
                this.postingSone = postingSone;
        }
 
        /**
-        * Returns the request that is currently being processed.
-        *
-        * @return The request being processed
-        */
-       public FreenetRequest getRequest() {
-               return request;
-       }
-
-       /**
         * Returns the Sone that provided the text that is being parsed.
         *
         * @return The posting Sone
index 76a475f..87d9816 100644 (file)
@@ -29,11 +29,11 @@ import net.pterodactylus.util.version.Version;
  */
 public class AboutPage extends SoneTemplatePage {
 
-       private final Version version;
+       private final String version;
        private final int year;
        private final String homepage;
 
-       public AboutPage(Template template, WebInterface webInterface, Version version, int year, String homepage) {
+       public AboutPage(Template template, WebInterface webInterface, String version, int year, String homepage) {
                super("about.html", template, "Page.About.Title", webInterface, false);
                this.version = version;
                this.year = year;
@@ -41,8 +41,7 @@ public class AboutPage extends SoneTemplatePage {
        }
 
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                templateContext.set("version", version);
                templateContext.set("year", year);
                templateContext.set("homepage", homepage);
index be5fc67..7602230 100644 (file)
@@ -50,8 +50,7 @@ public class BookmarkPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        String id = request.getHttpRequest().getPartAsStringFailsafe("post", 36);
                        String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
index 404abb1..b273bdb 100644 (file)
@@ -61,8 +61,7 @@ public class BookmarksPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                Set<Post> allPosts = webInterface.getCore().getBookmarkedPosts();
                Collection<Post> loadedPosts = Collections2.filter(allPosts, new Predicate<Post>() {
 
@@ -72,7 +71,7 @@ public class BookmarksPage extends SoneTemplatePage {
                        }
                });
                List<Post> sortedPosts = new ArrayList<Post>(loadedPosts);
-               Collections.sort(sortedPosts, Post.TIME_COMPARATOR);
+               Collections.sort(sortedPosts, Post.NEWEST_FIRST);
                Pagination<Post> pagination = new Pagination<Post>(sortedPosts, webInterface.getCore().getPreferences().getPostsPerPage()).setPage(parseInt(request.getHttpRequest().getParam("page"), 0));
                templateContext.set("pagination", pagination);
                templateContext.set("posts", pagination.getItems());
index 6ba14f0..7423b67 100644 (file)
@@ -53,8 +53,7 @@ public class CreateAlbumPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        String name = request.getHttpRequest().getPartAsStringFailsafe("name", 64).trim();
                        if (name.length() == 0) {
index f486379..ed478de 100644 (file)
@@ -54,8 +54,7 @@ public class CreatePostPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
                if (request.getMethod() == Method.POST) {
                        String text = request.getHttpRequest().getPartAsStringFailsafe("text", 65536).trim();
index 2383218..0bd5217 100644 (file)
@@ -54,8 +54,7 @@ public class CreateReplyPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                String postId = request.getHttpRequest().getPartAsStringFailsafe("post", 36);
                String text = request.getHttpRequest().getPartAsStringFailsafe("text", 65536).trim();
                String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
index 7ea74fe..a74204c 100644 (file)
@@ -96,8 +96,7 @@ public class CreateSonePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                List<Sone> localSones = new ArrayList<Sone>(webInterface.getCore().getLocalSones());
                Collections.sort(localSones, Sone.NICE_NAME_COMPARATOR);
                templateContext.set("sones", localSones);
index 7d2fa59..581266d 100644 (file)
@@ -46,8 +46,7 @@ public class DeleteAlbumPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", 36);
                        Album album = webInterface.getCore().getAlbum(albumId);
index eeb9e3c..e1b2f37 100644 (file)
@@ -50,8 +50,7 @@ public class DeleteImagePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                String imageId = (request.getMethod() == Method.POST) ? request.getHttpRequest().getPartAsStringFailsafe("image", 36) : request.getHttpRequest().getParam("image");
                Image image = webInterface.getCore().getImage(imageId, false);
                if (image == null) {
index 517a07e..c2bae56 100644 (file)
@@ -52,8 +52,7 @@ public class DeletePostPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.GET) {
                        String postId = request.getHttpRequest().getParam("post");
                        String returnPage = request.getHttpRequest().getParam("returnPage");
index f036489..a137b82 100644 (file)
@@ -52,8 +52,7 @@ public class DeleteProfileFieldPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                Sone currentSone = getCurrentSone(request.getToadletContext());
                Profile profile = currentSone.getProfile();
 
index 0cd6b4f..7112b73 100644 (file)
@@ -52,8 +52,7 @@ public class DeleteReplyPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                String replyId = request.getHttpRequest().getPartAsStringFailsafe("reply", 36);
                Optional<PostReply> reply = webInterface.getCore().getPostReply(replyId);
                String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
index ce01771..1390918 100644 (file)
@@ -52,8 +52,7 @@ public class DeleteSonePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        if (request.getHttpRequest().isPartSet("deleteSone")) {
                                Sone currentSone = getCurrentSone(request.getToadletContext());
index 80da869..c14a012 100644 (file)
@@ -51,8 +51,7 @@ public class DismissNotificationPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                String notificationId = request.getHttpRequest().getPartAsStringFailsafe("notification", 36);
                Optional<Notification> notification = webInterface.getNotification(notificationId);
                if (notification.isPresent() && notification.get().isDismissable()) {
index c09608a..50e3efb 100644 (file)
@@ -55,8 +55,7 @@ public class DistrustPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
                        String identity = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
index 6a08c3c..5829c8b 100644 (file)
@@ -48,8 +48,7 @@ public class EditAlbumPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        String albumId = request.getHttpRequest().getPartAsStringFailsafe("album", 36);
                        Album album = webInterface.getCore().getAlbum(albumId);
index b169161..419bc1d 100644 (file)
@@ -51,8 +51,7 @@ public class EditImagePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        String imageId = request.getHttpRequest().getPartAsStringFailsafe("image", 36);
                        String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
index 8cf18f2..e5bee78 100644 (file)
@@ -52,8 +52,7 @@ public class EditProfileFieldPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                Sone currentSone = getCurrentSone(request.getToadletContext());
                Profile profile = currentSone.getProfile();
 
index bf76c26..cffdeb9 100644 (file)
@@ -59,8 +59,7 @@ public class EditProfilePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                ToadletContext toadletContenxt = request.getToadletContext();
                Sone currentSone = getCurrentSone(toadletContenxt);
                Profile profile = currentSone.getProfile();
index eef94df..0652a52 100644 (file)
@@ -50,8 +50,7 @@ public class FollowSonePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
                        Sone currentSone = getCurrentSone(request.getToadletContext());
index 5da6e53..acc6354 100644 (file)
@@ -65,8 +65,7 @@ public class ImageBrowserPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                String albumId = request.getHttpRequest().getParam("album", null);
                if (albumId != null) {
                        Album album = webInterface.getCore().getAlbum(albumId);
index e5d5b7d..1d40f45 100644 (file)
@@ -58,8 +58,7 @@ public class IndexPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                final Sone currentSone = getCurrentSone(request.getToadletContext());
                Collection<Post> allPosts = new ArrayList<Post>();
                allPosts.addAll(currentSone.getPosts());
@@ -79,7 +78,7 @@ public class IndexPage extends SoneTemplatePage {
                }
                allPosts = Collections2.filter(allPosts, postVisibilityFilter.isVisible(currentSone));
                List<Post> sortedPosts = new ArrayList<Post>(allPosts);
-               Collections.sort(sortedPosts, Post.TIME_COMPARATOR);
+               Collections.sort(sortedPosts, Post.NEWEST_FIRST);
                Pagination<Post> pagination = new Pagination<Post>(sortedPosts, webInterface.getCore().getPreferences().getPostsPerPage()).setPage(parseInt(request.getHttpRequest().getParam("page"), 0));
                templateContext.set("pagination", pagination);
                templateContext.set("posts", pagination.getItems());
index d5fb9e2..da93a02 100644 (file)
@@ -65,8 +65,7 @@ public class KnownSonesPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                String sortField = request.getHttpRequest().getParam("sort", defaultSortField);
                String sortOrder = request.getHttpRequest().getParam("order", defaultSortOrder);
                String filter = request.getHttpRequest().getParam("filter");
index 171a7be..fc39eff 100644 (file)
@@ -51,8 +51,7 @@ public class LikePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        String type = request.getHttpRequest().getPartAsStringFailsafe("type", 16);
                        String id = request.getHttpRequest().getPartAsStringFailsafe(type, 36);
index 7881e9e..a0a9574 100644 (file)
@@ -50,8 +50,7 @@ public class LockSonePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
                Sone sone = webInterface.getCore().getLocalSone(soneId);
                if (sone != null) {
index 608f120..f9bd371 100644 (file)
@@ -63,8 +63,7 @@ public class LoginPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                /* get all own identities. */
                List<Sone> localSones = new ArrayList<Sone>(webInterface.getCore().getLocalSones());
                Collections.sort(localSones, Sone.NICE_NAME_COMPARATOR);
index d47044c..f7a254c 100644 (file)
@@ -47,9 +47,8 @@ public class LogoutPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                setCurrentSone(request.getToadletContext(), null);
-               super.processTemplate(request, templateContext);
                throw new RedirectException("index.html");
        }
 
index 39c5a43..dc1841b 100644 (file)
@@ -57,8 +57,7 @@ public class MarkAsKnownPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                String type = request.getHttpRequest().getPartAsStringFailsafe("type", 5);
                if (!type.equals("sone") && !type.equals("post") && !type.equals("reply")) {
                        throw new RedirectException("invalid.html");
index 2b99adb..406054d 100644 (file)
@@ -62,9 +62,7 @@ public class NewPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
-
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                /* collect new elements from notifications. */
                Set<Post> posts = new HashSet<Post>(webInterface.getNewPosts(getCurrentSone(request.getToadletContext(), false)));
                for (PostReply reply : webInterface.getNewReplies(getCurrentSone(request.getToadletContext(), false))) {
@@ -73,7 +71,7 @@ public class NewPage extends SoneTemplatePage {
 
                /* filter and sort them. */
                List<Post> sortedPosts = new ArrayList(posts);
-               Collections.sort(sortedPosts, Post.TIME_COMPARATOR);
+               Collections.sort(sortedPosts, Post.NEWEST_FIRST);
 
                /* paginate them. */
                Pagination<Post> pagination = new Pagination<Post>(sortedPosts, webInterface.getCore().getPreferences().getPostsPerPage()).setPage(parseInt(request.getHttpRequest().getParam("page"), 0));
index 0bc44d5..2bd1bfd 100644 (file)
@@ -58,8 +58,7 @@ public class OptionsPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                Preferences preferences = webInterface.getCore().getPreferences();
                Sone currentSone = webInterface.getCurrentSone(request.getToadletContext(), false);
                if (request.getMethod() == Method.POST) {
index 2383e0d..78a2b9e 100644 (file)
@@ -54,8 +54,7 @@ public class RescuePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                Sone currentSone = getCurrentSone(request.getToadletContext(), false);
                SoneRescuer soneRescuer = webInterface.getCore().getSoneRescuer(currentSone);
                if (request.getMethod() == Method.POST) {
index 5b657d5..8b444a7 100644 (file)
@@ -101,8 +101,7 @@ public class SearchPage extends SoneTemplatePage {
         */
        @Override
        @SuppressWarnings("synthetic-access")
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                String query = request.getHttpRequest().getParam("query").trim();
                if (query.length() == 0) {
                        throw new RedirectException("index.html");
@@ -593,7 +592,7 @@ public class SearchPage extends SoneTemplatePage {
 
                        @Override
                        public int compare(Hit<?> leftHit, Hit<?> rightHit) {
-                               return (rightHit.getScore() < leftHit.getScore()) ? -1 : ((rightHit.getScore() > leftHit.getScore()) ? 1 : 0);
+                               return Double.compare(rightHit.getScore(), leftHit.getScore());
                        }
 
                };
index b3e7a6a..bf9614d 100644 (file)
@@ -236,14 +236,14 @@ public class SoneTemplatePage extends FreenetTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
+       protected final void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                super.processTemplate(request, templateContext);
                Sone currentSone = getCurrentSone(request.getToadletContext(), false);
                templateContext.set("core", webInterface.getCore());
                templateContext.set("currentSone", currentSone);
                templateContext.set("localSones", webInterface.getCore().getLocalSones());
                templateContext.set("request", request);
-               templateContext.set("currentVersion", SonePlugin.VERSION);
+               templateContext.set("currentVersion", SonePlugin.getPluginVersion());
                templateContext.set("hasLatestVersion", webInterface.getCore().getUpdateChecker().hasLatestVersion());
                templateContext.set("latestEdition", webInterface.getCore().getUpdateChecker().getLatestEdition());
                templateContext.set("latestVersion", webInterface.getCore().getUpdateChecker().getLatestVersion());
@@ -252,6 +252,10 @@ public class SoneTemplatePage extends FreenetTemplatePage {
                Collections.sort(notifications, Notification.CREATED_TIME_SORTER);
                templateContext.set("notifications", notifications);
                templateContext.set("notificationHash", notifications.hashCode());
+               handleRequest(request, templateContext);
+       }
+
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
        }
 
        /**
index 637bb26..5b0bfbb 100644 (file)
@@ -55,8 +55,7 @@ public class TrustPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
                        String identity = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
index 51da550..8edf2da 100644 (file)
@@ -52,8 +52,7 @@ public class UnbookmarkPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        String id = request.getHttpRequest().getPartAsStringFailsafe("post", 36);
                        String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
index d8ff899..bff4013 100644 (file)
@@ -48,8 +48,7 @@ public class UnfollowSonePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
                        Sone currentSone = getCurrentSone(request.getToadletContext());
index ade8b89..a17502d 100644 (file)
@@ -51,8 +51,7 @@ public class UnlikePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        String type = request.getHttpRequest().getPartAsStringFailsafe("type", 16);
                        String id = request.getHttpRequest().getPartAsStringFailsafe(type, 36);
index f641361..7c36a88 100644 (file)
@@ -49,8 +49,7 @@ public class UnlockSonePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                String soneId = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
                Sone sone = webInterface.getCore().getLocalSone(soneId);
                if (sone != null) {
index 9d51276..cba3635 100644 (file)
@@ -55,8 +55,7 @@ public class UntrustPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        String returnPage = request.getHttpRequest().getPartAsStringFailsafe("returnPage", 256);
                        String identity = request.getHttpRequest().getPartAsStringFailsafe("sone", 44);
index 21c4272..467ceda 100644 (file)
@@ -79,8 +79,7 @@ public class UploadImagePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                if (request.getMethod() == Method.POST) {
                        Sone currentSone = getCurrentSone(request.getToadletContext());
                        String parentId = request.getHttpRequest().getPartAsStringFailsafe("parent", 36);
index dca59e2..56bac8d 100644 (file)
@@ -70,8 +70,7 @@ public class ViewPostPage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                String postId = request.getHttpRequest().getParam("post");
                boolean raw = request.getHttpRequest().getParam("raw").equals("true");
                Optional<Post> post = webInterface.getCore().getPost(postId);
index fe1e7db..f2c9c91 100644 (file)
@@ -80,8 +80,7 @@ public class ViewSonePage extends SoneTemplatePage {
         * {@inheritDoc}
         */
        @Override
-       protected void processTemplate(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
-               super.processTemplate(request, templateContext);
+       protected void handleRequest(FreenetRequest request, TemplateContext templateContext) throws RedirectException {
                String soneId = request.getHttpRequest().getParam("sone");
                Optional<Sone> sone = webInterface.getCore().getSone(soneId);
                templateContext.set("sone", sone.orNull());
@@ -91,7 +90,7 @@ public class ViewSonePage extends SoneTemplatePage {
                }
                List<Post> sonePosts = sone.get().getPosts();
                sonePosts.addAll(webInterface.getCore().getDirectedPosts(sone.get().getId()));
-               Collections.sort(sonePosts, Post.TIME_COMPARATOR);
+               Collections.sort(sonePosts, Post.NEWEST_FIRST);
                Pagination<Post> postPagination = new Pagination<Post>(sonePosts, webInterface.getCore().getPreferences().getPostsPerPage()).setPage(parseInt(request.getHttpRequest().getParam("postPage"), 0));
                templateContext.set("postPagination", postPagination);
                templateContext.set("posts", postPagination.getItems());
index 271111e..b7e3287 100644 (file)
@@ -717,7 +717,7 @@ public class WebInterface {
                pageToadlets.add(pageToadletFactory.createPageToadlet(new LogoutPage(emptyTemplate, this), "Logout"));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new OptionsPage(optionsTemplate, this), "Options"));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new RescuePage(rescueTemplate, this), "Rescue"));
-               pageToadlets.add(pageToadletFactory.createPageToadlet(new AboutPage(aboutTemplate, this, SonePlugin.VERSION, SonePlugin.getYear(), SonePlugin.getHomepage()), "About"));
+               pageToadlets.add(pageToadletFactory.createPageToadlet(new AboutPage(aboutTemplate, this, SonePlugin.getPluginVersion(), SonePlugin.getYear(), SonePlugin.getHomepage()), "About"));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("noPermission.html", noPermissionTemplate, "Page.NoPermission.Title", this)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("emptyImageTitle.html", emptyImageTitleTemplate, "Page.EmptyImageTitle.Title", this)));
                pageToadlets.add(pageToadletFactory.createPageToadlet(new SoneTemplatePage("emptyAlbumTitle.html", emptyAlbumTitleTemplate, "Page.EmptyAlbumTitle.Title", this)));
index d0cba79..65d3943 100644 (file)
@@ -113,7 +113,7 @@ public class GetNotificationsAjaxPage extends JsonPage {
                                templateContext.set("currentSone", webInterface.getCurrentSone(request.getToadletContext(), false));
                                templateContext.set("localSones", webInterface.getCore().getLocalSones());
                                templateContext.set("request", request);
-                               templateContext.set("currentVersion", SonePlugin.VERSION);
+                               templateContext.set("currentVersion", SonePlugin.getPluginVersion());
                                templateContext.set("hasLatestVersion", webInterface.getCore().getUpdateChecker().hasLatestVersion());
                                templateContext.set("latestEdition", webInterface.getCore().getUpdateChecker().getLatestEdition());
                                templateContext.set("latestVersion", webInterface.getCore().getUpdateChecker().getLatestVersion());
index a499019..ea21118 100644 (file)
@@ -1,7 +1,7 @@
 package net.pterodactylus.sone;
 
 import static java.util.UUID.randomUUID;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
index ec42a8c..7deb805 100644 (file)
@@ -13,8 +13,8 @@ import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
 import static org.hamcrest.Matchers.nullValue;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
index 8e83e21..cea8ed7 100644 (file)
@@ -1,11 +1,11 @@
 package net.pterodactylus.sone.core;
 
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.inOrder;
 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 net.pterodactylus.sone.core.Core.MarkPostKnown;
 import net.pterodactylus.sone.core.Core.MarkReplyKnown;
index 091c93f..8211c5e 100644 (file)
@@ -10,10 +10,10 @@ import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.notNullValue;
 import static org.hamcrest.Matchers.nullValue;
 import static org.mockito.ArgumentCaptor.forClass;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Matchers.anyShort;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyShort;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doNothing;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
@@ -69,6 +69,7 @@ import com.google.common.eventbus.EventBus;
 import org.junit.Before;
 import org.junit.Test;
 import org.mockito.ArgumentCaptor;
+import org.mockito.ArgumentMatchers;
 
 /**
  * Unit test for {@link FreenetInterface}.
@@ -198,7 +199,7 @@ public class FreenetInterfaceTest {
 
        @Test(expected = SoneException.class)
        public void insertExceptionIsForwardedAsSoneException() throws InsertException, SoneException {
-               when(highLevelSimpleClient.insertManifest(any(FreenetURI.class), any(HashMap.class), any(String.class))).thenThrow(InsertException.class);
+               when(highLevelSimpleClient.insertManifest(ArgumentMatchers.<FreenetURI>any(), ArgumentMatchers.<HashMap<String, Object>>any(), ArgumentMatchers.<String>any())).thenThrow(InsertException.class);
                freenetInterface.insertDirectory(null, null, null);
        }
 
index 0912391..75788d1 100644 (file)
@@ -1,7 +1,7 @@
 package net.pterodactylus.sone.core;
 
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
index a19fe2e..6756185 100644 (file)
@@ -5,7 +5,7 @@ import static net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.NO;
 import static net.pterodactylus.sone.fcp.FcpInterface.FullAccessRequired.WRITING;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.verifyNoMoreInteractions;
index f070eff..34e3573 100644 (file)
@@ -1,7 +1,7 @@
 package net.pterodactylus.sone.core;
 
 import static java.util.Arrays.asList;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.verify;
index 33c8ead..9043974 100644 (file)
@@ -9,8 +9,8 @@ import static net.pterodactylus.sone.data.Sone.SoneStatus.unknown;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
 import static org.mockito.ArgumentCaptor.forClass;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+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.times;
index 552746e..51947d1 100644 (file)
@@ -9,16 +9,16 @@ import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.nullValue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.argThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+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.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.hamcrest.MockitoHamcrest.argThat;
 
 import java.io.IOException;
 import java.util.HashMap;
@@ -260,7 +260,7 @@ public class SoneInserterTest {
                assertThat(manifestElement.getName(), is("test.txt"));
                assertThat(manifestElement.getMimeTypeOverride(), is("plain/text; charset=utf-8"));
                String templateContent = new String(toByteArray(manifestElement.getData().getInputStream()), Charsets.UTF_8);
-               assertThat(templateContent, containsString("Sone Version: " + SonePlugin.VERSION.toString() + "\n"));
+               assertThat(templateContent, containsString("Sone Version: " + SonePlugin.getPluginVersion() + "\n"));
                assertThat(templateContent, containsString("Core Startup: " + now + "\n"));
                assertThat(templateContent, containsString("Sone ID: " + "SoneId" + "\n"));
        }
index 2d8a5a0..da5ca50 100644 (file)
@@ -11,9 +11,9 @@ import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
 import static org.hamcrest.Matchers.notNullValue;
 import static org.hamcrest.Matchers.nullValue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyLong;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyLong;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
index ede6f13..0b339fb 100644 (file)
@@ -2,8 +2,8 @@ package net.pterodactylus.sone.core;
 
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.is;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+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;
index 44819ee..ad6b331 100644 (file)
@@ -5,13 +5,13 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.instanceOf;
 import static org.hamcrest.Matchers.is;
 import static org.mockito.ArgumentCaptor.forClass;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.argThat;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
+import static org.mockito.hamcrest.MockitoHamcrest.argThat;
 
 import java.io.IOException;
 import java.io.InputStream;
index aa810ee..7529fc6 100644 (file)
@@ -6,7 +6,7 @@ import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsString;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.not;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.doAnswer;
 import static org.mockito.Mockito.doThrow;
 import static org.mockito.Mockito.mock;
index 06c5b96..c16ce9d 100644 (file)
@@ -5,8 +5,8 @@ import static net.pterodactylus.sone.Matchers.isPostWithId;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.is;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
index a207132..d305031 100644 (file)
@@ -30,7 +30,7 @@ import static org.hamcrest.Matchers.contains;
 import static org.hamcrest.Matchers.containsInAnyOrder;
 import static org.hamcrest.Matchers.empty;
 import static org.hamcrest.Matchers.emptyIterable;
-import static org.mockito.Matchers.anyString;
+import static org.mockito.ArgumentMatchers.anyString;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.times;
index d583661..8d6ee61 100644 (file)
@@ -20,7 +20,7 @@ package net.pterodactylus.sone.fcp;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
index bd292dd..842d0e8 100644 (file)
@@ -20,7 +20,7 @@ package net.pterodactylus.sone.fcp;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
index f58c239..c5dc82b 100644 (file)
@@ -21,7 +21,7 @@ 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.Matchers.eq;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.verify;
 
index a07af28..72175bd 100644 (file)
@@ -26,8 +26,8 @@ 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.Matchers.any;
-import static org.mockito.Matchers.eq;
+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;
index b29b2c1..dabfd5a 100644 (file)
@@ -6,7 +6,7 @@ import static org.hamcrest.Matchers.emptyIterable;
 import static org.hamcrest.Matchers.hasSize;
 import static org.hamcrest.Matchers.is;
 import static org.hamcrest.Matchers.sameInstance;
-import static org.mockito.Matchers.any;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
@@ -29,6 +29,7 @@ import com.google.inject.Guice;
 import com.google.inject.Injector;
 import org.hamcrest.Matchers;
 import org.junit.Test;
+import org.mockito.ArgumentMatchers;
 
 /**
  * Unit test for {@link ListNotificationFilterTest}.
@@ -117,7 +118,7 @@ public class ListNotificationFilterTest {
        }
 
        private void setPostVisibilityPredicate(Predicate<Post> value) {
-               when(postVisibilityFilter.isVisible(any(Sone.class))).thenReturn(value);
+               when(postVisibilityFilter.isVisible(ArgumentMatchers.<Sone>any())).thenReturn(value);
        }
 
        @Test
index c354afa..7ae3dd8 100644 (file)
@@ -4,11 +4,11 @@ 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.Matchers.argThat;
-import static org.mockito.Matchers.eq;
+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;
 
index 20d2362..e96b6cb 100644 (file)
@@ -73,7 +73,7 @@ public class ImageLinkFilterTest {
                String result = String.valueOf(imageLinkFilter.format(templateContext, image, ImmutableMap.<String, Object>of()));
                Element imageElement = getSingleElement(result);
                assertThat(imageElement.attr("class"), is(""));
-               assertThat(imageElement.attr("src"), is("/image-key?forcedownload=true"));
+               assertThat(imageElement.attr("src"), is("/image-key"));
                assertThat(imageElement.attr("title"), is("image title"));
                assertThat(imageElement.attr("alt"), is("image description"));
                assertThat(imageElement.attr("width"), is("640"));
@@ -138,7 +138,7 @@ public class ImageLinkFilterTest {
                String result = String.valueOf(imageLinkFilter.format(templateContext, "image-id", ImmutableMap.<String, Object>of()));
                Element imageElement = getSingleElement(result);
                assertThat(imageElement.attr("class"), is(""));
-               assertThat(imageElement.attr("src"), is("/image-key?forcedownload=true"));
+               assertThat(imageElement.attr("src"), is("/image-key"));
                assertThat(imageElement.attr("title"), is("image title"));
                assertThat(imageElement.attr("alt"), is("image description"));
                assertThat(imageElement.attr("width"), is("640"));
diff --git a/src/test/java/net/pterodactylus/sone/test/Dirty.java b/src/test/java/net/pterodactylus/sone/test/Dirty.java
new file mode 100644 (file)
index 0000000..def311a
--- /dev/null
@@ -0,0 +1,20 @@
+package net.pterodactylus.sone.test;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation marks test methods that are somehow not good test methods.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+@Retention(SOURCE)
+@Target(METHOD)
+public @interface Dirty {
+
+       String value() default "";
+
+}
diff --git a/src/test/java/net/pterodactylus/sone/text/FreenetLinkPartTest.java b/src/test/java/net/pterodactylus/sone/text/FreenetLinkPartTest.java
new file mode 100644 (file)
index 0000000..f29fdde
--- /dev/null
@@ -0,0 +1,67 @@
+package net.pterodactylus.sone.text;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link FreenetLinkPart}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class FreenetLinkPartTest {
+
+       private final FreenetLinkPart part = new FreenetLinkPart("link", "text", "title", true);
+
+       @Test
+       public void linkIsRetainedCorrectly() {
+               assertThat(part.getLink(), is("link"));
+       }
+
+       @Test
+       public void textIsRetainedCorrectly() {
+               assertThat(part.getText(), is("text"));
+       }
+
+       @Test
+       public void titleIsRetainedCorrectly() {
+               assertThat(part.getTitle(), is("title"));
+       }
+
+       @Test
+       public void trustedIsRetainedCorrectly() {
+               assertThat(part.isTrusted(), is(true));
+       }
+
+       @Test
+       public void textIsUsedAsTitleIfNoTextIsGiven() {
+               assertThat(new FreenetLinkPart("link", "text", true).getTitle(), is("text"));
+       }
+
+       @Test(expected = NullPointerException.class)
+       public void nullIsNotAllowedForLink() {
+               new FreenetLinkPart(null, "text", "title", true);
+       }
+
+       @Test(expected = NullPointerException.class)
+       public void nullIsNotAllowedForText() {
+               new FreenetLinkPart("link", null, "title", true);
+       }
+
+       @Test(expected = NullPointerException.class)
+       public void nullIsNotAllowedForLinkInSecondaryConstructor() {
+               new FreenetLinkPart(null, "text", true);
+       }
+
+       @Test(expected = NullPointerException.class)
+       public void nullIsNotAllowedForTextInSecondaryConstructor() {
+               new FreenetLinkPart("link", null, true);
+       }
+
+       @Test(expected = NullPointerException.class)
+       public void nullIsNotAllowedForTitle() {
+               new FreenetLinkPart("link", "text", null, true);
+       }
+
+}
diff --git a/src/test/java/net/pterodactylus/sone/text/LinkPartTest.java b/src/test/java/net/pterodactylus/sone/text/LinkPartTest.java
new file mode 100644 (file)
index 0000000..431dfa6
--- /dev/null
@@ -0,0 +1,62 @@
+package net.pterodactylus.sone.text;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link LinkPart}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class LinkPartTest {
+
+       private final LinkPart part = new LinkPart("link", "text", "title");
+
+       @Test
+       public void linkIsRetainedCorrectly() {
+               assertThat(part.getLink(), is("link"));
+       }
+
+       @Test
+       public void textIsRetainedCorrectly() {
+               assertThat(part.getText(), is("text"));
+       }
+
+       @Test
+       public void titleIsRetainedCorrectly() {
+               assertThat(part.getTitle(), is("title"));
+       }
+
+       @Test
+       public void textIsUsedAsTitleIfNoTitleIsGiven() {
+               assertThat(new LinkPart("link", "text").getTitle(), is("text"));
+       }
+
+       @Test(expected = NullPointerException.class)
+       public void nullIsNotAllowedForLink() {
+               new LinkPart(null, "text", "title");
+       }
+
+       @Test(expected = NullPointerException.class)
+       public void nullIsNotAllowedForText() {
+               new LinkPart("link", null, "title");
+       }
+
+       @Test(expected = NullPointerException.class)
+       public void nullIsNotAllowedForLinkInSecondaryConstructor() {
+               new LinkPart(null, "text");
+       }
+
+       @Test(expected = NullPointerException.class)
+       public void nullIsNotAllowedForTextInSecondaryConstructor() {
+               new LinkPart("link", null);
+       }
+
+       @Test(expected = NullPointerException.class)
+       public void nullIsNotAllowedForTitle() {
+               new LinkPart("link", "text", null);
+       }
+
+}
diff --git a/src/test/java/net/pterodactylus/sone/text/PartContainerTest.java b/src/test/java/net/pterodactylus/sone/text/PartContainerTest.java
new file mode 100644 (file)
index 0000000..036b2dc
--- /dev/null
@@ -0,0 +1,99 @@
+package net.pterodactylus.sone.text;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link PartContainer}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class PartContainerTest {
+
+       private final PartContainer container = new PartContainer();
+
+       @Test
+       public void emptyContainerHasSizeZero() {
+               assertThat(container.size(), is(0));
+       }
+
+       @Test(expected = NullPointerException.class)
+       public void canNotAddNullPart() {
+           container.add(null);
+       }
+
+       @Test
+       public void containerWithSinglePartHasSizeOne() {
+               container.add(mock(Part.class));
+               assertThat(container.size(), is(1));
+       }
+
+       @Test
+       public void containerWithSinglePartCanReturnPart() {
+               Part part = mock(Part.class);
+               container.add(part);
+               assertThat(container.getPart(0), is(part));
+       }
+
+       @Test
+       public void containerIsEmptyAfterPartIsAddedAndRemoved() {
+               container.add(mock(Part.class));
+               container.removePart(0);
+               assertThat(container.size(), is(0));
+       }
+
+       @Test
+       public void containerContainsSecondPartIfFirstPartIsRemoved() {
+               container.add(mock(Part.class));
+               Part part = mock(Part.class);
+               container.add(part);
+               container.removePart(0);
+               assertThat(container.getPart(0), is(part));
+       }
+
+       @Test
+       public void textOfContainerPartIsTextOfPartsConcatenated() {
+               container.add(createPartWithText("first"));
+               container.add(createPartWithText("second"));
+               assertThat(container.getText(), is("firstsecond"));
+       }
+
+       private Part createPartWithText(String text) {
+               Part part = mock(Part.class);
+               when(part.getText()).thenReturn(text);
+               return part;
+       }
+
+       @Test(expected = NoSuchElementException.class)
+       public void emptyContainerIteratorThrowsOnNext() {
+               container.iterator().next();
+       }
+
+       @Test
+       public void iteratorIteratesPartsRecursivelyInCorrectOrder() {
+               Part firstPart = mock(Part.class);
+               PartContainer secondPart = new PartContainer();
+               Part thirdPart = mock(Part.class);
+               Part nestedFirstPart = mock(Part.class);
+               Part nestedSecondPart = mock(Part.class);
+               secondPart.add(nestedFirstPart);
+               secondPart.add(nestedSecondPart);
+               container.add(firstPart);
+               container.add(secondPart);
+               container.add(thirdPart);
+               Iterator<Part> parts = container.iterator();
+               assertThat(parts.next(), is(firstPart));
+               assertThat(parts.next(), is(nestedFirstPart));
+               assertThat(parts.next(), is(nestedSecondPart));
+               assertThat(parts.next(), is(thirdPart));
+               assertThat(parts.hasNext(), is(false));
+       }
+
+}
diff --git a/src/test/java/net/pterodactylus/sone/text/PlainTextPartTest.java b/src/test/java/net/pterodactylus/sone/text/PlainTextPartTest.java
new file mode 100644 (file)
index 0000000..72161ab
--- /dev/null
@@ -0,0 +1,27 @@
+package net.pterodactylus.sone.text;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link PlainTextPart}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class PlainTextPartTest {
+
+       private final PlainTextPart part = new PlainTextPart("text");
+
+       @Test
+       public void textIsRetainedCorrectly() {
+               assertThat(part.getText(), is("text"));
+       }
+
+       @Test(expected = NullPointerException.class)
+       public void nullIsNotAllowedForText() {
+           new PlainTextPart(null);
+       }
+
+}
diff --git a/src/test/java/net/pterodactylus/sone/text/PostPartTest.java b/src/test/java/net/pterodactylus/sone/text/PostPartTest.java
new file mode 100644 (file)
index 0000000..7b4ea19
--- /dev/null
@@ -0,0 +1,38 @@
+package net.pterodactylus.sone.text;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import net.pterodactylus.sone.data.Post;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link PostPart}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class PostPartTest {
+
+       private final Post post = mock(Post.class);
+       private final PostPart part = new PostPart(post);
+
+       @Test
+       public void postIsRetainedCorrectly() {
+               assertThat(part.getPost(), is(post));
+       }
+
+       @Test
+       public void textIsTakenFromPost() {
+               when(post.getText()).thenReturn("text");
+               assertThat(part.getText(), is("text"));
+       }
+
+       @Test(expected = NullPointerException.class)
+       public void nullIsNotAllowedForPost() {
+           new PostPart(null);
+       }
+
+}
diff --git a/src/test/java/net/pterodactylus/sone/text/SonePartTest.java b/src/test/java/net/pterodactylus/sone/text/SonePartTest.java
new file mode 100644 (file)
index 0000000..589acf7
--- /dev/null
@@ -0,0 +1,42 @@
+package net.pterodactylus.sone.text;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import net.pterodactylus.sone.data.Profile;
+import net.pterodactylus.sone.data.Sone;
+
+import org.hamcrest.MatcherAssert;
+import org.junit.Test;
+import org.mockito.Mockito;
+
+/**
+ * Unit test for {@link SonePart}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class SonePartTest {
+
+       private final Sone sone = mock(Sone.class);
+       private final SonePart part = new SonePart(sone);
+
+       @Test
+       public void soneIsRetainedCorrectly() {
+           assertThat(part.getSone(), is(sone));
+       }
+
+       @Test
+       public void textIsConstructedFromSonesNiceName() {
+           when(sone.getProfile()).thenReturn(mock(Profile.class));
+               when(sone.getName()).thenReturn("sone");
+               assertThat(part.getText(), is("sone"));
+       }
+
+       @Test(expected = NullPointerException.class)
+       public void nullIsNotAllowedForSone() {
+           new SonePart(null);
+       }
+
+}
index 7a06dcf..6483171 100644 (file)
 
 package net.pterodactylus.sone.text;
 
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.isIn;
+import static org.hamcrest.Matchers.notNullValue;
+
 import java.io.IOException;
-import java.util.Arrays;
 import java.util.Collection;
 
+import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.Sone;
 import net.pterodactylus.sone.data.impl.IdOnlySone;
+import net.pterodactylus.sone.database.PostProvider;
 import net.pterodactylus.sone.database.SoneProvider;
 
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
-import junit.framework.TestCase;
+import org.junit.Test;
 
 /**
  * JUnit test case for {@link SoneTextParser}.
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
  */
-public class SoneTextParserTest extends TestCase {
+public class SoneTextParserTest {
 
-       //
-       // ACTIONS
-       //
+       private final SoneTextParser soneTextParser = new SoneTextParser(null, null);
 
-       /**
-        * Tests basic plain-text operation of the parser.
-        *
-        * @throws IOException
-        *             if an I/O error occurs
-        */
        @SuppressWarnings("static-method")
+       @Test
        public void testPlainText() throws IOException {
-               SoneTextParser soneTextParser = new SoneTextParser(null, null);
-               Iterable<Part> parts;
-
                /* check basic operation. */
-               parts = soneTextParser.parse("Test.", null);
-               assertNotNull("Parts", parts);
-               assertEquals("Part Text", "Test.", convertText(parts, PlainTextPart.class));
+               Iterable<Part> parts = soneTextParser.parse("Test.", null);
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", convertText(parts, PlainTextPart.class), is("Test."));
 
                /* check empty lines at start and end. */
                parts = soneTextParser.parse("\nTest.\n\n", null);
-               assertNotNull("Parts", parts);
-               assertEquals("Part Text", "Test.", convertText(parts, PlainTextPart.class));
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", convertText(parts, PlainTextPart.class), is("Test."));
 
                /* check duplicate empty lines in the text. */
                parts = soneTextParser.parse("\nTest.\n\n\nTest.", null);
-               assertNotNull("Parts", parts);
-               assertEquals("Part Text", "Test.\n\nTest.", convertText(parts, PlainTextPart.class));
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", convertText(parts, PlainTextPart.class), is("Test.\n\nTest."));
+       }
+
+       @Test
+       public void consecutiveLinesAreSeparatedByLinefeed() {
+               Iterable<Part> parts = soneTextParser.parse("Text.\nText", null);
+               assertThat("Part Text", convertText(parts), is("Text.\nText"));
+       }
+
+       @Test
+       public void freenetLinksHaveTheFreenetPrefixRemoved() {
+               Iterable<Part> parts = soneTextParser.parse("freenet:KSK@gpl.txt", null);
+               assertThat("Part Text", convertText(parts), is("[KSK@gpl.txt|gpl.txt|gpl.txt]"));
+       }
+
+       @Test
+       public void onlyTheFirstItemInALineIsPrefixedWithALineBreak() {
+               Iterable<Part> parts = soneTextParser.parse("Text.\nKSK@gpl.txt and KSK@gpl.txt", null);
+               assertThat("Part Text", convertText(parts), is("Text.\n[KSK@gpl.txt|gpl.txt|gpl.txt] and [KSK@gpl.txt|gpl.txt|gpl.txt]"));
+       }
+
+       @Test
+       public void soneLinkWithTooShortSoneIdIsRenderedAsPlainText() {
+               Iterable<Part> parts = soneTextParser.parse("sone://too-short", null);
+               assertThat("Part Text", convertText(parts), is("sone://too-short"));
+       }
+
+       @Test
+       public void soneLinkIsRenderedCorrectlyIfSoneIsNotPresent() {
+               SoneTextParser parser = new SoneTextParser(new AbsentSoneProvider(), null);
+               Iterable<Part> parts = parser.parse("sone://DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU", null);
+               assertThat("Part Text", convertText(parts), is("[Sone|DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU]"));
+       }
+
+       @Test
+       public void postLinkIsRenderedAsPlainTextIfPostIdIsTooShort() {
+               Iterable<Part> parts = soneTextParser.parse("post://too-short", null);
+               assertThat("Part Text", convertText(parts), is("post://too-short"));
+       }
+
+       @Test
+       public void postLinkIsRenderedCorrectlyIfPostIsPresent() {
+               SoneTextParser parser = new SoneTextParser(null, new TestPostProvider());
+               Iterable<Part> parts = parser.parse("post://f3757817-b45a-497a-803f-9c5aafc10dc6", null);
+               assertThat("Part Text", convertText(parts), is("[Post|f3757817-b45a-497a-803f-9c5aafc10dc6|text]"));
+       }
+
+       @Test
+       public void postLinkIsRenderedAsPlainTextIfPostIsAbsent() {
+               SoneTextParser parser = new SoneTextParser(null, new AbsentPostProvider());
+               Iterable<Part> parts = parser.parse("post://f3757817-b45a-497a-803f-9c5aafc10dc6", null);
+               assertThat("Part Text", convertText(parts), is("post://f3757817-b45a-497a-803f-9c5aafc10dc6"));
+       }
+
+       @Test
+       public void nameOfFreenetLinkDoesNotContainUrlParameters() {
+           Iterable<Part> parts = soneTextParser.parse("KSK@gpl.txt?max-size=12345", null);
+               assertThat("Part Text", convertText(parts), is("[KSK@gpl.txt?max-size=12345|gpl.txt|gpl.txt]"));
+       }
+
+       @Test
+       public void trailingSlashInFreenetLinkIsRemovedForName() {
+               Iterable<Part> parts = soneTextParser.parse("KSK@gpl.txt/", null);
+               assertThat("Part Text", convertText(parts), is("[KSK@gpl.txt/|gpl.txt|gpl.txt]"));
+       }
+
+       @Test
+       public void lastMetaStringOfFreenetLinkIsUsedAsName() {
+               Iterable<Part> parts = soneTextParser.parse("CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/COPYING", null);
+               assertThat("Part Text", convertText(parts), is("[CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/COPYING|COPYING|COPYING]"));
+       }
+
+       @Test
+       public void freenetLinkWithoutMetaStringsAndDocNameGetsFirstNineCharactersOfKeyAsName() {
+               Iterable<Part> parts = soneTextParser.parse("CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8", null);
+               assertThat("Part Text", convertText(parts), is("[CHK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8|CHK@qM1nm|CHK@qM1nm]"));
+       }
+
+       @Test
+       public void malformedKeyIsRenderedAsPlainText() {
+               Iterable<Part> parts = soneTextParser.parse("CHK@qM1nmgU", null);
+               assertThat("Part Text", convertText(parts), is("CHK@qM1nmgU"));
+       }
+
+       @Test
+       public void httpsLinkHasItsPathsShortened() {
+               Iterable<Part> parts = soneTextParser.parse("https://test.test/some-long-path/file.txt", null);
+               assertThat("Part Text", convertText(parts), is("[https://test.test/some-long-path/file.txt|test.test/…/file.txt|test.test/…/file.txt]"));
+       }
+
+       @Test
+       public void httpLinksHaveTheirLastSlashRemoved() {
+           Iterable<Part> parts = soneTextParser.parse("http://test.test/test/", null);
+               assertThat("Part Text", convertText(parts), is("[http://test.test/test/|test.test/…|test.test/…]"));
+       }
+
+       @Test
+       public void wwwPrefixIsRemovedForHostnameWithTwoDotsAndNoPath() {
+               Iterable<Part> parts = soneTextParser.parse("http://www.test.test", null);
+               assertThat("Part Text", convertText(parts), is("[http://www.test.test|test.test|test.test]"));
+       }
+
+       @Test
+       public void wwwPrefixIsRemovedForHostnameWithTwoDotsAndAPath() {
+               Iterable<Part> parts = soneTextParser.parse("http://www.test.test/test.html", null);
+               assertThat("Part Text", convertText(parts), is("[http://www.test.test/test.html|test.test/test.html|test.test/test.html]"));
+       }
+
+       @Test
+       public void hostnameIsKeptIntactIfNotBeginningWithWww() {
+               Iterable<Part> parts = soneTextParser.parse("http://test.test.test/test.html", null);
+               assertThat("Part Text", convertText(parts), is("[http://test.test.test/test.html|test.test.test/test.html|test.test.test/test.html]"));
+       }
+
+       @Test
+       public void hostnameWithOneDotButNoSlashIsKeptIntact() {
+               Iterable<Part> parts = soneTextParser.parse("http://test.test", null);
+               assertThat("Part Text", convertText(parts), is("[http://test.test|test.test|test.test]"));
+       }
+
+       @Test
+       public void urlParametersAreRemovedForHttpLinks() {
+               Iterable<Part> parts = soneTextParser.parse("http://test.test?foo=bar", null);
+               assertThat("Part Text", convertText(parts), is("[http://test.test?foo=bar|test.test|test.test]"));
+       }
+
+       @Test
+       public void emptyStringIsParsedCorrectly() {
+               Iterable<Part> parts = soneTextParser.parse("", null);
+               assertThat("Part Text", convertText(parts), is(""));
+       }
+
+       @Test
+       public void linksAreParsedInCorrectOrder() {
+               Iterable<Part> parts = soneTextParser.parse("KSK@ CHK@", null);
+               assertThat("Part Text", convertText(parts), is("KSK@ CHK@"));
+       }
+
+       @Test
+       public void sskLinkWithoutContextIsNotTrusted() {
+               Iterable<Part> parts = soneTextParser.parse("SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test", null);
+               assertThat("Part Text", convertText(parts), is("[SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|test|test]"));
+       }
+
+       @Test
+       public void sskLinkWithContextWithoutSoneIsNotTrusted() {
+               SoneTextParserContext context = new SoneTextParserContext(null);
+               Iterable<Part> parts = soneTextParser.parse("SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test", context);
+               assertThat("Part Text", convertText(parts), is("[SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|test|test]"));
+       }
+
+       @Test
+       public void sskLinkWithContextWithDifferentSoneIsNotTrusted() {
+               SoneTextParserContext context = new SoneTextParserContext(new IdOnlySone("DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU"));
+               Iterable<Part> parts = soneTextParser.parse("SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test", context);
+               assertThat("Part Text", convertText(parts), is("[SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|test|test]"));
+       }
+
+       @Test
+       public void sskLinkWithContextWithCorrectSoneIsTrusted() {
+               SoneTextParserContext context = new SoneTextParserContext(new IdOnlySone("qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU"));
+               Iterable<Part> parts = soneTextParser.parse("SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test", context);
+               assertThat("Part Text", convertText(parts), is("[SSK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test|trusted|test|test]"));
+       }
+
+       @Test
+       public void uskLinkWithContextWithCorrectSoneIsTrusted() {
+               SoneTextParserContext context = new SoneTextParserContext(new IdOnlySone("qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU"));
+               Iterable<Part> parts = soneTextParser.parse("USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0", context);
+               assertThat("Part Text", convertText(parts), is("[USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0|trusted|test|test]"));
        }
 
-       /**
-        * Tests parsing of KSK links.
-        *
-        * @throws IOException
-        *             if an I/O error occurs
-        */
        @SuppressWarnings("static-method")
+       @Test
        public void testKSKLinks() throws IOException {
-               SoneTextParser soneTextParser = new SoneTextParser(null, null);
-               Iterable<Part> parts;
-
                /* check basic links. */
-               parts = soneTextParser.parse("KSK@gpl.txt", null);
-               assertNotNull("Parts", parts);
-               assertEquals("Part Text", "[KSK@gpl.txt|gpl.txt|gpl.txt]", convertText(parts, FreenetLinkPart.class));
+               Iterable<Part> parts = soneTextParser.parse("KSK@gpl.txt", null);
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", convertText(parts, FreenetLinkPart.class), is("[KSK@gpl.txt|gpl.txt|gpl.txt]"));
 
                /* check embedded links. */
                parts = soneTextParser.parse("Link is KSK@gpl.txt\u200b.", null);
-               assertNotNull("Parts", parts);
-               assertEquals("Part Text", "Link is [KSK@gpl.txt|gpl.txt|gpl.txt]\u200b.", convertText(parts, PlainTextPart.class, FreenetLinkPart.class));
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", convertText(parts, PlainTextPart.class, FreenetLinkPart.class), is("Link is [KSK@gpl.txt|gpl.txt|gpl.txt]\u200b."));
 
                /* check embedded links and line breaks. */
                parts = soneTextParser.parse("Link is KSK@gpl.txt\nKSK@test.dat\n", null);
-               assertNotNull("Parts", parts);
-               assertEquals("Part Text", "Link is [KSK@gpl.txt|gpl.txt|gpl.txt]\n[KSK@test.dat|test.dat|test.dat]", convertText(parts, PlainTextPart.class, FreenetLinkPart.class));
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", convertText(parts, PlainTextPart.class, FreenetLinkPart.class), is("Link is [KSK@gpl.txt|gpl.txt|gpl.txt]\n[KSK@test.dat|test.dat|test.dat]"));
        }
 
-       /**
-        * Test case for a bug that was discovered in 0.6.7.
-        *
-        * @throws IOException
-        *             if an I/O error occurs
-        */
        @SuppressWarnings({ "synthetic-access", "static-method" })
+       @Test
        public void testEmptyLinesAndSoneLinks() throws IOException {
                SoneTextParser soneTextParser = new SoneTextParser(new TestSoneProvider(), null);
-               Iterable<Part> parts;
 
                /* check basic links. */
-               parts = soneTextParser.parse("Some text.\n\nLink to sone://DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU and stuff.", null);
-               assertNotNull("Parts", parts);
-               assertEquals("Part Text", "Some text.\n\nLink to [Sone|DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU] and stuff.", convertText(parts, PlainTextPart.class, SonePart.class));
+               Iterable<Part> parts = soneTextParser.parse("Some text.\n\nLink to sone://DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU and stuff.", null);
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", convertText(parts, PlainTextPart.class, SonePart.class), is("Some text.\n\nLink to [Sone|DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU] and stuff."));
        }
 
-       /**
-        * Test for a bug discovered in Sone 0.8.4 where a plain “http://” would be
-        * parsed into a link.
-        *
-        * @throws IOException
-        *             if an I/O error occurs
-        */
        @SuppressWarnings({ "synthetic-access", "static-method" })
+       @Test
        public void testEmpyHttpLinks() throws IOException {
                SoneTextParser soneTextParser = new SoneTextParser(new TestSoneProvider(), null);
-               Iterable<Part> parts;
 
                /* check empty http links. */
-               parts = soneTextParser.parse("Some text. Empty link: http:// – nice!", null);
-               assertNotNull("Parts", parts);
-               assertEquals("Part Text", "Some text. Empty link: http:// – nice!", convertText(parts, PlainTextPart.class));
+               Iterable<Part> parts = soneTextParser.parse("Some text. Empty link: http:// – nice!", null);
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", convertText(parts, PlainTextPart.class), is("Some text. Empty link: http:// – nice!"));
+       }
+
+       @Test
+       public void httpLinkWithoutParensEndsAtNextClosingParen() {
+               Iterable<Part> parts = soneTextParser.parse("Some text (and a link: http://example.sone/abc) – nice!", null);
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", convertText(parts, PlainTextPart.class, LinkPart.class), is("Some text (and a link: [http://example.sone/abc|example.sone/abc|example.sone/abc]) – nice!"));
+       }
+
+       @Test
+       public void uskLinkEndsAtFirstNonNumericNonSlashCharacterAfterVersionNumber() {
+               Iterable<Part> parts = soneTextParser.parse("Some link (USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0). Nice", null);
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", convertText(parts), is("Some link ([USK@qM1nmgU-YUnIttmEhqjTl7ifAF3Z6o~5EPwQW03uEQU,aztSUkT-VT1dWvfSUt9YpfyW~Flmf5yXpBnIE~v8sAg,AAMC--8/test/0|test|test]). Nice"));
+       }
+
+       @Test
+       public void httpLinkWithOpenedAndClosedParensEndsAtNextClosingParen() {
+               Iterable<Part> parts = soneTextParser.parse("Some text (and a link: http://example.sone/abc_(def)) – nice!", null);
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", convertText(parts, PlainTextPart.class, LinkPart.class), is("Some text (and a link: [http://example.sone/abc_(def)|example.sone/abc_(def)|example.sone/abc_(def)]) – nice!"));
+       }
+
+       @Test
+       public void punctuationIsIgnoredAtEndOfLinkBeforeWhitespace() {
+               SoneTextParser soneTextParser = new SoneTextParser(null, null);
+               Iterable<Part> parts = soneTextParser.parse("Some text and a link: http://example.sone/abc. Nice!", null);
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", convertText(parts, PlainTextPart.class, LinkPart.class), is("Some text and a link: [http://example.sone/abc|example.sone/abc|example.sone/abc]. Nice!"));
        }
 
-       //
-       // PRIVATE METHODS
-       //
+       @Test
+       public void multiplePunctuationCharactersAreIgnoredAtEndOfLinkBeforeWhitespace() {
+               Iterable<Part> parts = soneTextParser.parse("Some text and a link: http://example.sone/abc... Nice!", null);
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", convertText(parts, PlainTextPart.class, LinkPart.class), is("Some text and a link: [http://example.sone/abc|example.sone/abc|example.sone/abc]... Nice!"));
+       }
+
+       @Test
+       public void commasAreIgnoredAtEndOfLinkBeforeWhitespace() {
+               SoneTextParser soneTextParser = new SoneTextParser(null, null);
+               Iterable<Part> parts = soneTextParser.parse("Some text and a link: http://example.sone/abc, nice!", null);
+               assertThat("Parts", parts, notNullValue());
+               assertThat("Part Text", convertText(parts, PlainTextPart.class, LinkPart.class), is("Some text and a link: [http://example.sone/abc|example.sone/abc|example.sone/abc], nice!"));
+       }
 
        /**
         * Converts all given {@link Part}s into a string, validating that the
@@ -147,16 +331,9 @@ public class SoneTextParserTest extends TestCase {
        private static String convertText(Iterable<Part> parts, Class<?>... validClasses) {
                StringBuilder text = new StringBuilder();
                for (Part part : parts) {
-                       assertNotNull("Part", part);
-                       boolean classValid = validClasses.length == 0;
-                       for (Class<?> validClass : validClasses) {
-                               if (validClass.isAssignableFrom(part.getClass())) {
-                                       classValid = true;
-                                       break;
-                               }
-                       }
-                       if (!classValid) {
-                               fail("Part’s Class (" + part.getClass() + ") is not one of " + Arrays.toString(validClasses));
+                       assertThat("Part", part, notNullValue());
+                       if (validClasses.length != 0) {
+                               assertThat("Part’s class", part.getClass(), isIn(validClasses));
                        }
                        if (part instanceof PlainTextPart) {
                                text.append(((PlainTextPart) part).getText());
@@ -169,6 +346,9 @@ public class SoneTextParserTest extends TestCase {
                        } else if (part instanceof SonePart) {
                                SonePart sonePart = (SonePart) part;
                                text.append("[Sone|").append(sonePart.getSone().getId()).append(']');
+                       } else if (part instanceof PostPart) {
+                               PostPart postPart = (PostPart) part;
+                               text.append("[Post|").append(postPart.getPost().getId()).append("|").append(postPart.getPost().getText()).append("]");
                        }
                }
                return text.toString();
@@ -225,4 +405,86 @@ public class SoneTextParserTest extends TestCase {
 
        }
 
+       private static class AbsentSoneProvider extends TestSoneProvider {
+
+               @Override
+               public Optional<Sone> getSone(String soneId) {
+                       return Optional.absent();
+               }
+
+       }
+
+       private static class TestPostProvider implements PostProvider {
+
+               @Override
+               public Optional<Post> getPost(final String postId) {
+                       return Optional.<Post>of(new Post() {
+                               @Override
+                               public String getId() {
+                                       return postId;
+                               }
+
+                               @Override
+                               public boolean isLoaded() {
+                                       return false;
+                               }
+
+                               @Override
+                               public Sone getSone() {
+                                       return null;
+                               }
+
+                               @Override
+                               public Optional<String> getRecipientId() {
+                                       return null;
+                               }
+
+                               @Override
+                               public Optional<Sone> getRecipient() {
+                                       return null;
+                               }
+
+                               @Override
+                               public long getTime() {
+                                       return 0;
+                               }
+
+                               @Override
+                               public String getText() {
+                                       return "text";
+                               }
+
+                               @Override
+                               public boolean isKnown() {
+                                       return false;
+                               }
+
+                               @Override
+                               public Post setKnown(boolean known) {
+                                       return null;
+                               }
+                       });
+               }
+
+               @Override
+               public Collection<Post> getPosts(String soneId) {
+                       return null;
+               }
+
+               @Override
+               public Collection<Post> getDirectedPosts(String recipientId) {
+                       return null;
+               }
+
+       }
+
+       private static class AbsentPostProvider extends TestPostProvider {
+
+               @Override
+               public Optional<Post> getPost(String postId) {
+                       return Optional.absent();
+               }
+
+       }
+
 }
diff --git a/src/test/java/net/pterodactylus/sone/web/AboutPageTest.java b/src/test/java/net/pterodactylus/sone/web/AboutPageTest.java
new file mode 100644 (file)
index 0000000..df41e61
--- /dev/null
@@ -0,0 +1,43 @@
+package net.pterodactylus.sone.web;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link AboutPage}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class AboutPageTest extends WebPageTest {
+
+       private final String version = "0.1.2";
+       private final int year = 1234;
+       private final String homepage = "home://page";
+       private final AboutPage page = new AboutPage(template, webInterface, version, year, homepage);
+
+       @Test
+       public void pageReturnsCorrectPath() {
+               assertThat(page.getPath(), is("about.html"));
+       }
+
+       @Test
+       public void pageSetsCorrectVersionInTemplateContext() throws Exception {
+               page.processTemplate(freenetRequest, templateContext);
+               assertThat(templateContext.get("version"), is((Object) version));
+       }
+
+       @Test
+       public void pageSetsCorrectHomepageInTemplateContext() throws Exception {
+               page.processTemplate(freenetRequest, templateContext);
+               assertThat(templateContext.get("homepage"), is((Object) homepage));
+       }
+
+       @Test
+       public void pageSetsCorrectYearInTemplateContext() throws Exception {
+               page.processTemplate(freenetRequest, templateContext);
+               assertThat(templateContext.get("year"), is((Object) year));
+       }
+
+}
diff --git a/src/test/java/net/pterodactylus/sone/web/BookmarkPageTest.java b/src/test/java/net/pterodactylus/sone/web/BookmarkPageTest.java
new file mode 100644 (file)
index 0000000..ddba2c5
--- /dev/null
@@ -0,0 +1,67 @@
+package net.pterodactylus.sone.web;
+
+import static net.pterodactylus.sone.web.WebTestUtils.redirectsTo;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.util.web.Method;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link BookmarkPage}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class BookmarkPageTest extends WebPageTest {
+
+       private final BookmarkPage page = new BookmarkPage(template, webInterface);
+
+       @Test
+       public void pathIsSetCorrectly() {
+               assertThat(page.getPath(), is("bookmark.html"));
+       }
+
+       @Test
+       public void getRequestDoesNotBookmarkAnythingAndDoesNotRedirect() throws Exception {
+               page.processTemplate(freenetRequest, templateContext);
+               verify(core, never()).bookmarkPost(any(Post.class));
+       }
+
+       @Test
+       public void postIsBookmarkedCorrectly() throws Exception {
+               setupRequest();
+               Post post = mock(Post.class);
+               addPost("post-id", post);
+               expectedException.expect(redirectsTo("return-page.html"));
+               try {
+                       page.processTemplate(freenetRequest, templateContext);
+               } finally {
+                       verify(core).bookmarkPost(post);
+               }
+       }
+
+       private void setupRequest() {
+               request("", Method.POST);
+               addHttpRequestParameter("post", "post-id");
+               addHttpRequestParameter("returnPage", "return-page.html");
+       }
+
+       @Test
+       public void nonExistentPostIsNotBookmarked() throws Exception {
+               setupRequest();
+               addPost("post-id", null);
+               expectedException.expect(redirectsTo("return-page.html"));
+               try {
+                       page.processTemplate(freenetRequest, templateContext);
+               } finally {
+                       verify(core, never()).bookmarkPost(any(Post.class));
+               }
+       }
+
+}
diff --git a/src/test/java/net/pterodactylus/sone/web/BookmarksPageTest.java b/src/test/java/net/pterodactylus/sone/web/BookmarksPageTest.java
new file mode 100644 (file)
index 0000000..46b0a18
--- /dev/null
@@ -0,0 +1,78 @@
+package net.pterodactylus.sone.web;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.Collection;
+import java.util.HashSet;
+import java.util.Set;
+
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.web.page.FreenetTemplatePage.RedirectException;
+import net.pterodactylus.util.collection.Pagination;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link BookmarksPage}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class BookmarksPageTest extends WebPageTest {
+
+       private final BookmarksPage page = new BookmarksPage(template, webInterface);
+
+       @Test
+       public void pageReturnsCorrectPath() {
+               assertThat(page.getPath(), is("bookmarks.html"));
+       }
+
+       @Test
+       @SuppressWarnings("unchecked")
+       public void pageSetsCorrectPostsInTemplateContext() throws RedirectException {
+               Post post1 = createPost(true, 3000L);
+               Post post2 = createPost(true, 1000L);
+               Post post3 = createPost(true, 2000L);
+               Set<Post> bookmarkedPosts = createBookmarkedPosts(post1, post2, post3);
+               when(core.getBookmarkedPosts()).thenReturn(bookmarkedPosts);
+               when(core.getPreferences().getPostsPerPage()).thenReturn(5);
+               page.processTemplate(freenetRequest, templateContext);
+               assertThat((Collection<Post>) templateContext.get("posts"), contains(post1, post3, post2));
+               assertThat(((Pagination<Post>) templateContext.get("pagination")).getItems(), contains(post1, post3, post2));
+               assertThat(((Boolean) templateContext.get("postsNotLoaded")), is(false));
+       }
+
+       private Set<Post> createBookmarkedPosts(Post post1, Post post2, Post post3) {
+               Set<Post> bookmarkedPosts = new HashSet<>();
+               bookmarkedPosts.add(post1);
+               bookmarkedPosts.add(post2);
+               bookmarkedPosts.add(post3);
+               return bookmarkedPosts;
+       }
+
+       @Test
+       @SuppressWarnings("unchecked")
+       public void notLoadedPostsAreNotIncludedButAFlagIsSet() throws RedirectException {
+               Post post1 = createPost(true, 1000L);
+               Post post2 = createPost(true, 3000L);
+               Post post3 = createPost(false, 2000L);
+               Set<Post> bookmarkedPosts = createBookmarkedPosts(post1, post2, post3);
+               when(core.getBookmarkedPosts()).thenReturn(bookmarkedPosts);
+               when(core.getPreferences().getPostsPerPage()).thenReturn(5);
+               page.processTemplate(freenetRequest, templateContext);
+               assertThat((Collection<Post>) templateContext.get("posts"), contains(post2, post1));
+               assertThat(((Pagination<Post>) templateContext.get("pagination")).getItems(), contains(post2, post1));
+               assertThat(((Boolean) templateContext.get("postsNotLoaded")), is(true));
+       }
+
+       private Post createPost(boolean postLoaded, long time) {
+               Post post = mock(Post.class);
+               when(post.isLoaded()).thenReturn(postLoaded);
+               when(post.getTime()).thenReturn(time);
+               return post;
+       }
+
+}
diff --git a/src/test/java/net/pterodactylus/sone/web/CreateAlbumPageTest.java b/src/test/java/net/pterodactylus/sone/web/CreateAlbumPageTest.java
new file mode 100644 (file)
index 0000000..da0ec0c
--- /dev/null
@@ -0,0 +1,105 @@
+package net.pterodactylus.sone.web;
+
+import static net.pterodactylus.sone.web.WebTestUtils.redirectsTo;
+import static net.pterodactylus.util.web.Method.POST;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Answers.RETURNS_DEEP_STUBS;
+import static org.mockito.Answers.RETURNS_SELF;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import net.pterodactylus.sone.data.Album;
+import net.pterodactylus.sone.data.Album.Modifier;
+import net.pterodactylus.sone.data.Album.Modifier.AlbumTitleMustNotBeEmpty;
+import net.pterodactylus.sone.test.Dirty;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link CreateAlbumPage}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class CreateAlbumPageTest extends WebPageTest {
+
+       private final CreateAlbumPage page = new CreateAlbumPage(template, webInterface);
+
+       @Test
+       public void pageReturnsCorrectPath() {
+               assertThat(page.getPath(), is("createAlbum.html"));
+       }
+
+       @Test
+       public void getRequestShowsTemplate() throws Exception {
+               page.processTemplate(freenetRequest, templateContext);
+       }
+
+       @Test
+       public void missingNameResultsInAttributeSetInTemplateContext() throws Exception {
+               request("", POST);
+               page.processTemplate(freenetRequest, templateContext);
+               assertThat(templateContext.get("nameMissing"), is((Object) true));
+       }
+
+       @Test
+       public void titleAndDescriptionAreSetCorrectlyOnTheAlbum() throws Exception {
+               request("", POST);
+               Album parentAlbum = createAlbum("parent-id");
+               when(core.getAlbum("parent-id")).thenReturn(parentAlbum);
+               Album newAlbum = createAlbum("album-id");
+               when(core.createAlbum(currentSone, parentAlbum)).thenReturn(newAlbum);
+               addHttpRequestParameter("name", "new name");
+               addHttpRequestParameter("description", "new description");
+               addHttpRequestParameter("parent", "parent-id");
+               expectedException.expect(redirectsTo("imageBrowser.html?album=album-id"));
+               try {
+                       page.processTemplate(freenetRequest, templateContext);
+               } finally {
+                       verify(newAlbum).modify();
+                       verify(newAlbum.modify()).setTitle("new name");
+                       verify(newAlbum.modify()).setDescription("new description");
+                       verify(newAlbum.modify()).update();
+                       verify(core).touchConfiguration();
+               }
+       }
+
+       private Album createAlbum(String albumId) {
+               Album newAlbum = mock(Album.class, RETURNS_DEEP_STUBS);
+               when(newAlbum.getId()).thenReturn(albumId);
+               Modifier albumModifier = mock(Modifier.class, RETURNS_SELF);
+               when(newAlbum.modify()).thenReturn(albumModifier);
+               when(albumModifier.update()).thenReturn(newAlbum);
+               return newAlbum;
+       }
+
+       @Test
+       public void rootAlbumIsUsedIfNoParentIsSpecified() throws Exception {
+               request("", POST);
+               Album parentAlbum = createAlbum("root-id");
+               when(currentSone.getRootAlbum()).thenReturn(parentAlbum);
+               Album newAlbum = createAlbum("album-id");
+               when(core.createAlbum(currentSone, parentAlbum)).thenReturn(newAlbum);
+               addHttpRequestParameter("name", "new name");
+               addHttpRequestParameter("description", "new description");
+               expectedException.expect(redirectsTo("imageBrowser.html?album=album-id"));
+               page.processTemplate(freenetRequest, templateContext);
+       }
+
+       @Test
+       @Dirty("that exception can never happen")
+       public void emptyAlbumTitleRedirectsToErrorPage() throws Exception {
+               request("", POST);
+               Album parentAlbum = createAlbum("root-id");
+               when(currentSone.getRootAlbum()).thenReturn(parentAlbum);
+               Album newAlbum = createAlbum("album-id");
+               when(core.createAlbum(currentSone, parentAlbum)).thenReturn(newAlbum);
+               when(newAlbum.modify().update()).thenThrow(AlbumTitleMustNotBeEmpty.class);
+               addHttpRequestParameter("name", "new name");
+               addHttpRequestParameter("description", "new description");
+               expectedException.expect(redirectsTo("emptyAlbumTitle.html"));
+               page.processTemplate(freenetRequest, templateContext);
+       }
+
+}
diff --git a/src/test/java/net/pterodactylus/sone/web/CreatePostPageTest.java b/src/test/java/net/pterodactylus/sone/web/CreatePostPageTest.java
new file mode 100644 (file)
index 0000000..2a91086
--- /dev/null
@@ -0,0 +1,89 @@
+package net.pterodactylus.sone.web;
+
+import static net.pterodactylus.util.web.Method.POST;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+
+import net.pterodactylus.sone.data.Sone;
+
+import com.google.common.base.Optional;
+import org.junit.Test;
+
+/**
+ * Unit test for {@link CreatePostPage}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class CreatePostPageTest extends WebPageTest {
+
+       private final CreatePostPage page = new CreatePostPage(template, webInterface);
+
+       @Test
+       public void pageReturnsCorrectPath() {
+               assertThat(page.getPath(), is("createPost.html"));
+       }
+
+       @Test
+       public void returnPageIsSetInTemplateContext() throws Exception {
+               addHttpRequestParameter("returnPage", "returnPage.html");
+               page.processTemplate(freenetRequest, templateContext);
+               assertThat(templateContext.get("returnPage"), is((Object) "returnPage.html"));
+       }
+
+       @Test
+       public void postIsCreatedCorrectly() throws Exception {
+               addHttpRequestParameter("returnPage", "returnPage.html");
+               addHttpRequestParameter("text", "post text");
+               request("", POST);
+               expectedException.expect(WebTestUtils.redirectsTo("returnPage.html"));
+               try {
+                       page.processTemplate(freenetRequest, templateContext);
+               } finally {
+                       verify(core).createPost(currentSone, Optional.<Sone>absent(), "post text");
+               }
+       }
+
+       @Test
+       public void creatingAnEmptyPostIsDenied() throws Exception {
+               addHttpRequestParameter("returnPage", "returnPage.html");
+               addHttpRequestParameter("text", "   ");
+               request("", POST);
+               page.processTemplate(freenetRequest, templateContext);
+               assertThat(templateContext.get("errorTextEmpty"), is((Object) true));
+       }
+
+       @Test
+       public void aSenderCanBeSelected() throws Exception {
+               addHttpRequestParameter("returnPage", "returnPage.html");
+               addHttpRequestParameter("text", "post text");
+               addHttpRequestParameter("sender", "sender-id");
+               Sone sender = mock(Sone.class);
+               addLocalSone("sender-id", sender);
+               request("", POST);
+               expectedException.expect(WebTestUtils.redirectsTo("returnPage.html"));
+               try {
+                       page.processTemplate(freenetRequest, templateContext);
+               } finally {
+                       verify(core).createPost(sender, Optional.<Sone>absent(), "post text");
+               }
+       }
+
+       @Test
+       public void aRecipientCanBeSelected() throws Exception {
+               addHttpRequestParameter("returnPage", "returnPage.html");
+               addHttpRequestParameter("text", "post text");
+               addHttpRequestParameter("recipient", "recipient-id");
+               Sone recipient = mock(Sone.class);
+               addSone("recipient-id", recipient);
+               request("", POST);
+               expectedException.expect(WebTestUtils.redirectsTo("returnPage.html"));
+               try {
+                       page.processTemplate(freenetRequest, templateContext);
+               } finally {
+                       verify(core).createPost(currentSone, Optional.of(recipient), "post text");
+               }
+       }
+
+}
diff --git a/src/test/java/net/pterodactylus/sone/web/CreateReplyPageTest.java b/src/test/java/net/pterodactylus/sone/web/CreateReplyPageTest.java
new file mode 100644 (file)
index 0000000..996f5c0
--- /dev/null
@@ -0,0 +1,107 @@
+package net.pterodactylus.sone.web;
+
+import static net.pterodactylus.sone.web.WebTestUtils.redirectsTo;
+import static net.pterodactylus.util.web.Method.GET;
+import static net.pterodactylus.util.web.Method.POST;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
+
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.Sone;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link CreateReplyPageTest}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class CreateReplyPageTest extends WebPageTest {
+
+       private final CreateReplyPage page = new CreateReplyPage(template, webInterface);
+
+       @Test
+       public void pageReturnsCorrectPath() {
+               assertThat(page.getPath(), is("createReply.html"));
+       }
+
+       @Test
+       public void replyIsCreatedCorrectly() throws Exception {
+               request("", POST);
+               addHttpRequestParameter("post", "post-id");
+               addHttpRequestParameter("text", "some text");
+               addHttpRequestParameter("returnPage", "returnPage.html");
+               Post post = mock(Post.class);
+               addPost("post-id", post);
+               expectedException.expect(redirectsTo("returnPage.html"));
+               try {
+                       page.processTemplate(freenetRequest, templateContext);
+               } finally {
+                       verify(core).createReply(currentSone, post, "some text");
+               }
+       }
+
+       @Test
+       public void replyIsCreatedWithCorrectSender() throws Exception {
+               request("", POST);
+               addHttpRequestParameter("post", "post-id");
+               addHttpRequestParameter("text", "some text");
+               addHttpRequestParameter("returnPage", "returnPage.html");
+               addHttpRequestParameter("sender", "sender-id");
+               Sone sender = mock(Sone.class);
+               addLocalSone("sender-id", sender);
+               Post post = mock(Post.class);
+               addPost("post-id", post);
+               expectedException.expect(redirectsTo("returnPage.html"));
+               try {
+                       page.processTemplate(freenetRequest, templateContext);
+               } finally {
+                       verify(core).createReply(sender, post, "some text");
+               }
+       }
+
+       @Test
+       public void emptyTextSetsVariableInTemplateContext() throws Exception {
+               request("", POST);
+               addPost("post-id", mock(Post.class));
+               addHttpRequestParameter("post", "post-id");
+               addHttpRequestParameter("text", "   ");
+               addHttpRequestParameter("returnPage", "returnPage.html");
+               page.processTemplate(freenetRequest, templateContext);
+               assertThat(templateContext.<Boolean>get("errorTextEmpty", Boolean.class), is(true));
+               verifyParametersAreCopied("");
+               verify(core, never()).createReply(any(Sone.class), any(Post.class), anyString());
+       }
+
+       private void verifyParametersAreCopied(String text) {
+               assertThat(templateContext.<String>get("postId", String.class), is("post-id"));
+               assertThat(templateContext.<String>get("text", String.class), is(text));
+               assertThat(templateContext.<String>get("returnPage", String.class), is("returnPage.html"));
+       }
+
+       @Test
+       public void userIsRedirectIfPostDoesNotExist() throws Exception {
+               request("", POST);
+               addHttpRequestParameter("post", "post-id");
+               addHttpRequestParameter("text", "some text");
+               addHttpRequestParameter("returnPage", "returnPage.html");
+               expectedException.expect(redirectsTo("noPermission.html"));
+               page.processTemplate(freenetRequest, templateContext);
+       }
+
+       @Test
+       public void getRequestServesTemplateAndStoresParameters() throws Exception {
+               request("", GET);
+               addHttpRequestParameter("post", "post-id");
+               addHttpRequestParameter("text", "some text");
+               addHttpRequestParameter("returnPage", "returnPage.html");
+               page.processTemplate(freenetRequest, templateContext);
+               verifyParametersAreCopied("some text");
+       }
+
+}
diff --git a/src/test/java/net/pterodactylus/sone/web/CreateSonePageTest.java b/src/test/java/net/pterodactylus/sone/web/CreateSonePageTest.java
new file mode 100644 (file)
index 0000000..aab40c9
--- /dev/null
@@ -0,0 +1,167 @@
+package net.pterodactylus.sone.web;
+
+import static net.pterodactylus.sone.web.WebTestUtils.redirectsTo;
+import static net.pterodactylus.util.web.Method.POST;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.contains;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+
+import net.pterodactylus.sone.data.Profile;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.freenet.wot.OwnIdentity;
+
+import org.junit.Test;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Unit test for {@link CreateSonePage}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class CreateSonePageTest extends WebPageTest {
+
+       private final CreateSonePage page = new CreateSonePage(template, webInterface);
+       private final Sone[] localSones = { createSone("local-sone1"), createSone("local-sone2"), createSone("local-sone3") };
+       private final OwnIdentity[] ownIdentities = {
+                       createOwnIdentity("own-id-1", "Sone"),
+                       createOwnIdentity("own-id-2", "Test", "Foo"),
+                       createOwnIdentity("own-id-3"),
+                       createOwnIdentity("own-id-4", "Sone")
+       };
+
+       @Test
+       public void pageReturnsCorrectPath() {
+               assertThat(page.getPath(), is("createSone.html"));
+       }
+
+       @Test
+       @SuppressWarnings("unchecked")
+       public void getRequestStoresListOfIdentitiesInTemplateContext() throws Exception {
+               addDefaultLocalSones();
+               addDefaultOwnIdentities();
+               page.processTemplate(freenetRequest, templateContext);
+               assertThat((Collection<Sone>) templateContext.get("sones"), contains(localSones[0], localSones[1], localSones[2]));
+               assertThat((Collection<OwnIdentity>) templateContext.get("identitiesWithoutSone"), contains(ownIdentities[1], ownIdentities[2]));
+       }
+
+       private void addDefaultLocalSones() {
+               addLocalSone("local-sone3", localSones[2]);
+               addLocalSone("local-sone1", localSones[0]);
+               addLocalSone("local-sone2", localSones[1]);
+       }
+
+       private void addDefaultOwnIdentities() {
+               addOwnIdentity(ownIdentities[2]);
+               addOwnIdentity(ownIdentities[0]);
+               addOwnIdentity(ownIdentities[3]);
+               addOwnIdentity(ownIdentities[1]);
+       }
+
+       private Sone createSone(String id) {
+               Sone sone = mock(Sone.class);
+               when(sone.getId()).thenReturn(id);
+               when(sone.getProfile()).thenReturn(new Profile(sone));
+               return sone;
+       }
+
+       private OwnIdentity createOwnIdentity(String id, final String... contexts) {
+               OwnIdentity ownIdentity = mock(OwnIdentity.class);
+               when(ownIdentity.getId()).thenReturn(id);
+               when(ownIdentity.getNickname()).thenReturn(id);
+               when(ownIdentity.getContexts()).thenReturn(new HashSet<>(Arrays.asList(contexts)));
+               when(ownIdentity.hasContext(anyString())).thenAnswer(new Answer<Boolean>() {
+                       @Override
+                       public Boolean answer(InvocationOnMock invocation) throws Throwable {
+                               return Arrays.asList(contexts).contains(invocation.<String>getArgument(0));
+                       }
+               });
+               return ownIdentity;
+       }
+
+       @Test
+       public void soneIsCreatedAndLoggedIn() throws Exception {
+               addDefaultLocalSones();
+               addDefaultOwnIdentities();
+               addHttpRequestParameter("identity", "own-id-3");
+               request("", POST);
+               Sone newSone = mock(Sone.class);
+               when(core.createSone(ownIdentities[2])).thenReturn(newSone);
+               expectedException.expect(redirectsTo("index.html"));
+               try {
+                       page.processTemplate(freenetRequest, templateContext);
+               } finally {
+                       verify(core).createSone(ownIdentities[2]);
+                       verify(webInterface).setCurrentSone(toadletContext, newSone);
+               }
+       }
+
+       @Test
+       public void onInvalidIdentityIdFlagIsStoredInTemplateContext() throws Exception {
+               addDefaultLocalSones();
+               addDefaultOwnIdentities();
+               addHttpRequestParameter("identity", "own-id-invalid");
+               request("", POST);
+               page.processTemplate(freenetRequest, templateContext);
+               assertThat(((Boolean) templateContext.get("errorNoIdentity")), is(true));
+       }
+
+       @Test
+       public void ifSoneIsNotCreatedUserIsStillRedirectedToIndex() throws Exception {
+               addDefaultLocalSones();
+               addDefaultOwnIdentities();
+               addHttpRequestParameter("identity", "own-id-3");
+               request("", POST);
+               when(core.createSone(ownIdentities[2])).thenReturn(null);
+               expectedException.expect(redirectsTo("index.html"));
+               try {
+                       page.processTemplate(freenetRequest, templateContext);
+               } finally {
+                       verify(core).createSone(ownIdentities[2]);
+                       verify(webInterface).setCurrentSone(toadletContext, null);
+               }
+       }
+
+       @Test
+       public void doNotShowCreateSoneInMenuIfFullAccessRequiredButClientHasNoFullAccess() {
+               when(core.getPreferences().isRequireFullAccess()).thenReturn(true);
+               when(toadletContext.isAllowedFullAccess()).thenReturn(false);
+               assertThat(page.isEnabled(toadletContext), is(false));
+       }
+
+       @Test
+       public void showCreateSoneInMenuIfNotLoggedInAndClientHasFullAccess() {
+               when(core.getPreferences().isRequireFullAccess()).thenReturn(true);
+               when(toadletContext.isAllowedFullAccess()).thenReturn(true);
+               unsetCurrentSone();
+               assertThat(page.isEnabled(toadletContext), is(true));
+       }
+
+       @Test
+       public void showCreateSoneInMenuIfNotLoggedIn() {
+               unsetCurrentSone();
+               assertThat(page.isEnabled(toadletContext), is(true));
+       }
+
+       @Test
+       public void showCreateSoneInMenuIfLoggedInAndASingleSoneExists() {
+               addLocalSone("local-sone", mock(Sone.class));
+               assertThat(page.isEnabled(toadletContext), is(true));
+       }
+
+       @Test
+       public void doNotShowCreateSoneInMenuIfLoggedInAndMoreLocalSonesExists() {
+               addLocalSone("local-sone1", mock(Sone.class));
+               addLocalSone("local-sone2", mock(Sone.class));
+               assertThat(page.isEnabled(toadletContext), is(false));
+       }
+
+}
index 80c8a2f..88c87a1 100644 (file)
@@ -1,61 +1,28 @@
 package net.pterodactylus.sone.web;
 
 import static net.pterodactylus.sone.web.WebTestUtils.redirectsTo;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
-import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
-import static org.mockito.Mockito.mock;
+import static net.pterodactylus.util.web.Method.POST;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.when;
 
-import java.util.Collections;
-
 import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.notify.Notification;
-import net.pterodactylus.util.template.Template;
-import net.pterodactylus.util.template.TemplateContext;
-import net.pterodactylus.util.web.Method;
-
-import freenet.support.api.HTTPRequest;
 
 import com.google.common.base.Optional;
-import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.mockito.Matchers;
 
 /**
  * Unit test for {@link DeleteReplyPage}.
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
  */
-public class DeleteReplyPageTest {
+public class DeleteReplyPageTest extends WebPageTest {
 
-       @Rule
-       public final ExpectedException expectedException = ExpectedException.none();
-
-       private final Template template = new Template();
-       private final WebInterface webInterface = mock(WebInterface.class, RETURNS_DEEP_STUBS);
        private final DeleteReplyPage page = new DeleteReplyPage(template, webInterface);
-       private final TemplateContext templateContext = new TemplateContext();
-       private final FreenetRequest freenetRequest = mock(FreenetRequest.class);
-       private final HTTPRequest httpRequest = mock(HTTPRequest.class);
-
-       @Before
-       public void setupWebInterface() {
-               when(webInterface.getNotifications(Matchers.any(Sone.class))).thenReturn(Collections.<Notification>emptyList());
-       }
-
-       @Before
-       public void setupHttpRequest() {
-               when(freenetRequest.getHttpRequest()).thenReturn(httpRequest);
-       }
 
        @Test
        public void tryingToDeleteAReplyWithAnInvalidIdResultsInNoPermissionPage() throws Exception {
-               when(freenetRequest.getMethod()).thenReturn(Method.POST);
+               request("", POST);
                when(httpRequest.getPartAsStringFailsafe(eq("reply"), anyInt())).thenReturn("id");
                when(webInterface.getCore().getPostReply("id")).thenReturn(Optional.<PostReply>absent());
                expectedException.expect(redirectsTo("noPermission.html"));
index b527e58..e110969 100644 (file)
@@ -3,24 +3,13 @@ package net.pterodactylus.sone.web;
 import static java.util.Arrays.asList;
 import static org.hamcrest.MatcherAssert.assertThat;
 import static org.hamcrest.Matchers.containsInAnyOrder;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyBoolean;
-import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import java.util.Collections;
 import java.util.List;
 
 import net.pterodactylus.sone.data.Post;
 import net.pterodactylus.sone.data.PostReply;
-import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.notify.Notification;
-import net.pterodactylus.util.template.Template;
-import net.pterodactylus.util.template.TemplateContext;
-
-import freenet.clients.http.ToadletContext;
 
 import com.google.common.base.Optional;
 import org.junit.Before;
@@ -31,25 +20,13 @@ import org.junit.Test;
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
  */
-public class NewPageTest {
+public class NewPageTest extends WebPageTest {
 
-       private final Template template = mock(Template.class);
-       private final WebInterface webInterface = mock(WebInterface.class, RETURNS_DEEP_STUBS);
        private final NewPage newPage = new NewPage(template, webInterface);
-       private final Sone currentSone = mock(Sone.class);
-       private final TemplateContext templateContext = new TemplateContext();
-       private final FreenetRequest freenetRequest = mock(FreenetRequest.class, RETURNS_DEEP_STUBS);
-
-       @Before
-       public void setupFreenetRequest() {
-               when(freenetRequest.getToadletContext()).thenReturn(mock(ToadletContext.class));
-       }
 
        @Before
-       public void setupWebInterface() {
+       public void setupNumberOfPostsPerPage() {
                when(webInterface.getCore().getPreferences().getPostsPerPage()).thenReturn(5);
-               when(webInterface.getCurrentSone(any(ToadletContext.class), anyBoolean())).thenReturn(currentSone);
-               when(webInterface.getNotifications(any(Sone.class))).thenReturn(Collections.<Notification>emptyList());
        }
 
        @Test
index e74efbd..3b8258e 100644 (file)
@@ -1,61 +1,29 @@
 package net.pterodactylus.sone.web;
 
 import static net.pterodactylus.sone.web.WebTestUtils.redirectsTo;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyInt;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.when;
 
-import java.net.URI;
-
-import net.pterodactylus.sone.core.Core;
-import net.pterodactylus.sone.core.UpdateChecker;
 import net.pterodactylus.sone.data.Album;
-import net.pterodactylus.sone.data.Sone;
-import net.pterodactylus.sone.web.page.FreenetRequest;
-import net.pterodactylus.util.template.Template;
-import net.pterodactylus.util.template.TemplateContext;
 import net.pterodactylus.util.web.Method;
 
-import freenet.clients.http.ToadletContext;
-import freenet.support.api.HTTPRequest;
-
 import org.junit.Before;
-import org.junit.Rule;
 import org.junit.Test;
-import org.junit.rules.ExpectedException;
 
 /**
  * Unit test for {@link UploadImagePageTest}.
  *
  * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
  */
-public class UploadImagePageTest {
-
-       @Rule
-       public final ExpectedException expectedException = ExpectedException.none();
+public class UploadImagePageTest extends WebPageTest {
 
-       private final Template template = new Template();
-       private final WebInterface webInterface = mock(WebInterface.class);
        private final UploadImagePage uploadImagePage = new UploadImagePage(template, webInterface);
 
-       private final TemplateContext templateContext = new TemplateContext();
-       private final HTTPRequest httpRequest = mock(HTTPRequest.class);
-       private final ToadletContext toadletContext = mock(ToadletContext.class);
-       private final Core core = mock(Core.class);
-       private final Sone currentSone = mock(Sone.class);
        private final Album parentAlbum = mock(Album.class);
 
        @Before
-       public void setupWebInterface() {
-               UpdateChecker updateChecker = mock(UpdateChecker.class);
-               when(core.getUpdateChecker()).thenReturn(updateChecker);
-               when(webInterface.getCore()).thenReturn(core);
-               when(webInterface.getCurrentSone(any(ToadletContext.class))).thenReturn(currentSone);
-       }
-
-       @Before
        public void setupParentAlbum() {
                when(core.getAlbum("parent-id")).thenReturn(parentAlbum);
                when(parentAlbum.getSone()).thenReturn(currentSone);
@@ -63,11 +31,11 @@ public class UploadImagePageTest {
 
        @Test
        public void uploadingAnImageWithoutTitleRedirectsToEmptyImageTitlePage() throws Exception {
-               FreenetRequest request = new FreenetRequest(new URI(""), Method.POST, httpRequest, toadletContext);
+               request("", Method.POST);
                when(httpRequest.getPartAsStringFailsafe(eq("parent"), anyInt())).thenReturn("parent-id");
                when(httpRequest.getPartAsStringFailsafe(eq("title"), anyInt())).thenReturn("  ");
                expectedException.expect(redirectsTo("emptyImageTitle.html"));
-               uploadImagePage.processTemplate(request, templateContext);
+               uploadImagePage.processTemplate(freenetRequest, templateContext);
        }
 
 }
diff --git a/src/test/java/net/pterodactylus/sone/web/WebPageTest.java b/src/test/java/net/pterodactylus/sone/web/WebPageTest.java
new file mode 100644 (file)
index 0000000..3e2df88
--- /dev/null
@@ -0,0 +1,138 @@
+package net.pterodactylus.sone.web;
+
+import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.anyString;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import net.pterodactylus.sone.core.Core;
+import net.pterodactylus.sone.core.UpdateChecker;
+import net.pterodactylus.sone.data.Post;
+import net.pterodactylus.sone.data.Sone;
+import net.pterodactylus.sone.freenet.wot.OwnIdentity;
+import net.pterodactylus.sone.web.page.FreenetRequest;
+import net.pterodactylus.util.notify.Notification;
+import net.pterodactylus.util.template.Template;
+import net.pterodactylus.util.template.TemplateContext;
+import net.pterodactylus.util.web.Method;
+
+import freenet.clients.http.ToadletContext;
+import freenet.support.api.HTTPRequest;
+
+import com.google.common.base.Optional;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.rules.ExpectedException;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+/**
+ * Base class for web page tests.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public abstract class WebPageTest {
+
+       @Rule
+       public final ExpectedException expectedException = ExpectedException.none();
+
+       protected final Template template = new Template();
+       protected final WebInterface webInterface = mock(WebInterface.class, RETURNS_DEEP_STUBS);
+       protected final Core core = webInterface.getCore();
+
+       protected final Sone currentSone = mock(Sone.class);
+
+       protected final TemplateContext templateContext = new TemplateContext();
+       protected final HTTPRequest httpRequest = mock(HTTPRequest.class);
+       protected final FreenetRequest freenetRequest = mock(FreenetRequest.class);
+       protected final ToadletContext toadletContext = mock(ToadletContext.class);
+
+       private final Set<OwnIdentity> ownIdentities = new HashSet<>();
+       private final List<Sone> localSones = new ArrayList<>();
+
+       @Before
+       public final void setupFreenetRequest() {
+               when(freenetRequest.getToadletContext()).thenReturn(toadletContext);
+               when(freenetRequest.getHttpRequest()).thenReturn(httpRequest);
+               when(httpRequest.getPartAsStringFailsafe(anyString(), anyInt())).thenAnswer(new Answer<String>() {
+                       @Override
+                       public String answer(InvocationOnMock invocation) throws Throwable {
+                               return "";
+                       }
+               });
+       }
+
+       @Before
+       public final void setupCore() {
+               UpdateChecker updateChecker = mock(UpdateChecker.class);
+               when(core.getUpdateChecker()).thenReturn(updateChecker);
+               when(core.getLocalSone(anyString())).thenReturn(null);
+               when(core.getLocalSones()).thenReturn(localSones);
+               when(core.getSone(anyString())).thenReturn(Optional.<Sone>absent());
+               when(core.getPost(anyString())).thenReturn(Optional.<Post>absent());
+       }
+
+       @Before
+       public final void setupIdentityManager() {
+               when(core.getIdentityManager().getAllOwnIdentities()).thenReturn(ownIdentities);
+       }
+
+       @Before
+       public final void setupWebInterface() {
+               when(webInterface.getCurrentSone(toadletContext)).thenReturn(currentSone);
+               when(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(currentSone);
+               when(webInterface.getNotifications(currentSone)).thenReturn(new ArrayList<Notification>());
+       }
+
+       protected void unsetCurrentSone() {
+               when(webInterface.getCurrentSone(toadletContext)).thenReturn(null);
+               when(webInterface.getCurrentSone(eq(toadletContext), anyBoolean())).thenReturn(null);
+       }
+
+       protected void request(String uri, Method method) {
+               try {
+                       when(freenetRequest.getUri()).thenReturn(new URI(uri));
+               } catch (URISyntaxException e) {
+                       throw new RuntimeException(e);
+               }
+               when(freenetRequest.getMethod()).thenReturn(method);
+       }
+
+       protected void addHttpRequestParameter(String name, final String value) {
+               when(httpRequest.getPartAsStringFailsafe(eq(name), anyInt())).thenAnswer(new Answer<String>() {
+                       @Override
+                       public String answer(InvocationOnMock invocation) throws Throwable {
+                               int maxLength = invocation.getArgument(1);
+                               return value.substring(0, Math.min(maxLength, value.length()));
+                       }
+               });
+       }
+
+       protected void addPost(String postId, Post post) {
+               when(core.getPost(postId)).thenReturn(Optional.fromNullable(post));
+       }
+
+       protected void addSone(String soneId, Sone sone) {
+               when(core.getSone(eq(soneId))).thenReturn(Optional.fromNullable(sone));
+       }
+
+       protected void addLocalSone(String soneId, Sone sone) {
+               when(core.getLocalSone(eq(soneId))).thenReturn(sone);
+               localSones.add(sone);
+       }
+
+       protected void addOwnIdentity(OwnIdentity ownIdentity) {
+               ownIdentities.add(ownIdentity);
+       }
+
+}
index 5f2025f..d7c2053 100644 (file)
@@ -8,12 +8,9 @@ import static com.google.common.base.Optional.of;
 import static org.hamcrest.CoreMatchers.is;
 import static org.hamcrest.CoreMatchers.notNullValue;
 import static org.junit.Assert.assertThat;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.anyString;
-import static org.mockito.Matchers.argThat;
+import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 import static org.mockito.Mockito.when;
 
diff --git a/src/test/java/net/pterodactylus/sone/web/page/FreenetRequestTest.java b/src/test/java/net/pterodactylus/sone/web/page/FreenetRequestTest.java
new file mode 100644 (file)
index 0000000..ac01e51
--- /dev/null
@@ -0,0 +1,54 @@
+package net.pterodactylus.sone.web.page;
+
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+import static org.mockito.Mockito.mock;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+
+import net.pterodactylus.util.web.Method;
+
+import freenet.clients.http.ToadletContext;
+import freenet.support.api.HTTPRequest;
+
+import org.junit.Test;
+
+/**
+ * Unit test for {@link FreenetRequest}.
+ *
+ * @author <a href="mailto:bombe@pterodactylus.net">David ‘Bombe’ Roden</a>
+ */
+public class FreenetRequestTest {
+
+       private final URI uri = new URI(".");
+       private final Method method = Method.GET;
+       private final HTTPRequest httpRequest = mock(HTTPRequest.class);
+       private final ToadletContext toadletContext = mock(ToadletContext.class);
+       private final FreenetRequest request = new FreenetRequest(uri, method, httpRequest, toadletContext);
+
+       @SuppressWarnings("unused")
+       public FreenetRequestTest() throws URISyntaxException {
+       }
+
+       @Test
+       public void uriIsRetainedCorrectly() {
+               assertThat(request.getUri(), is(uri));
+       }
+
+       @Test
+       public void methodIsRetainedCorrectly() {
+               assertThat(request.getMethod(), is(method));
+       }
+
+       @Test
+       public void httpRequestIsRetainedCorrectly() {
+               assertThat(request.getHttpRequest(), is(httpRequest));
+       }
+
+       @Test
+       public void toadletContextIsRetainedCorrectly() {
+               assertThat(request.getToadletContext(), is(toadletContext));
+       }
+
+}
diff --git a/template.txt b/template.txt
deleted file mode 100644 (file)
index a26591c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-<%if foo>foo<%else>bar<%/if>
\ No newline at end of file