Merge pull request #34 from kemitix/tidy-up

Tidy up
This commit is contained in:
Paul Campbell 2018-09-01 15:33:33 +01:00 committed by GitHub
commit 41e90e35d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 189 additions and 384 deletions

View file

@ -1,6 +1,25 @@
CHANGELOG CHANGELOG
========= =========
0.5.0
-----
* Compatible with Java 10
* Bump kemitix-parent from 1.1.0 to 5.1.1
* Use kemitix-checkstyle-ruleset 4.1.1
* Don't throw generic exceptions
* Add mon 0.12.0 as dependency
* Bump tiles-maven-plugin from 2.11 to 2.12
* [testing] Use a free port for test mail server
* [testing] Bump mockito-core from 1.10.19 to 2.21.0
* [testing] Bump spring-context-support from 4.2.6.RELEASE to 5.0.8.RELEASE
* [testing] Bump simple-java-mail from 3.0.1 to 3.1.1
* [admin] Simpliify .gitignore
* [admin] Remove old netbeans config
* [admin] Switch to trunk based development
* [admin] Stop submitting coverage reports to coveralls
* [admin] Switch to Jenkins for CI/CD
0.4.0 0.4.0
------ ------

View file

@ -1,207 +0,0 @@
<?xml version="1.0"?>
<!DOCTYPE module PUBLIC
"-//Puppy Crawl//DTD Check Configuration 1.3//EN"
"http://www.puppycrawl.com/dtds/configuration_1_3.dtd">
<!-- Template checkstyle rules primarily intented for use with projects based on the kemitix-parent POM -->
<!-- -->
<!-- See http://checkstyle.sourceforge.net/checks.html for documentation on each rule. -->
<module name="Checker">
<module name="JavadocPackage"/> <!-- package-info.java must exist -->
<module name="NewlineAtEndOfFile">
<property name="lineSeparator" value="lf"/> <!-- must use unix line endings -->
</module>
<module name="FileLength"/> <!-- files must be less than 2000 lines -->
<module name="FileTabCharacter"/> <!-- tabs not allowed -->
<module name="RegexpSingleline">
<property name="format" value="\s+$"/>
<property name="message" value="Line has trailing spaces."/>
</module>
<module name="SuppressWarningsFilter"/> <!-- enable @SuppressWarnings for checkstyle rules -->
<module name="TreeWalker">
<module name="AbbreviationAsWordInName"/> <!-- enforce proper CamelCase -->
<module name="AbstractClassName"/> <!-- enforce Abstract.* in abstract class names -->
<module name="AnnotationLocation"/> <!-- annotations should be on line by themselves -->
<module name="AnnotationUseStyle"/> <!-- annotations should only use () and named attributes when needed -->
<module name="AnonInnerLength"/> <!-- limits anonymous inner classes to 20 lines -->
<module name="ArrayTrailingComma"/> <!-- arrays should have a trailing comma (unless braces are on same line)-->
<module name="ArrayTypeStyle"/> <!-- enforce Java style arrays -->
<module name="AtclauseOrder"/> <!-- enforce standard order for javadoc elements -->
<module name="AvoidEscapedUnicodeCharacters"> <!-- prevent use of obscure escape codes -->
<property name="allowEscapesForControlCharacters" value="true"/> <!-- unless non-printable controls -->
</module>
<module name="AvoidNestedBlocks"/> <!-- avoid unnecessary blocks {} -->
<module name="AvoidStarImport"/> <!-- import package.* is not allowed -->
<module name="AvoidStaticImport"> <!-- import static ... is not allowed -->
<property name="excludes"
value="org.assertj.core.api.Assertions.assertThat,org.mockito.BDDMockito.given,org.mockito.Mockito.*,org.mockito.Matchers.*,org.mockito.Mockito.*"/> <!-- unless selected testing shorthands -->
</module>
<module name="BooleanExpressionComplexity"> <!-- restrict number of &&, ||, &, | and ^ expressions -->
<property name="max" value="2"/>
</module>
<module name="CatchParameterName"/> <!-- restrict parameter names when catching an Exception -->
<module name="ClassDataAbstractionCoupling"/> <!-- restrict number of classes instantiated per class to 7 -->
<module name="ClassFanOutComplexity"/> <!-- restrict class dependencies to 20 -->
<module name="ClassTypeParameterName"/> <!-- restrict class type parameters (i.e. generics) to ^[A-Z]$ -->
<module name="ConstantName"/> <!-- force all uppercase for static final fields -->
<module name="CommentsIndentation"/> <!-- enforce comment indentation to match surrounding code -->
<module name="CovariantEquals"/> <!-- ensure correct version of equals() is implemented -->
<module name="CyclomaticComplexity"> <!-- limit complexity score -->
<property name="max" value="5"/>
</module>
<module name="DeclarationOrder"/> <!-- enforce order: static variables, variables, constructors, methods -->
<module name="DefaultComesLast"/> <!-- enforce default as last option in switch statement -->
<module name="DesignForExtension"/> <!-- protect against bad subclassing - use interfaces for mocking in unit tests -->
<module name="EmptyBlock"/> <!-- checks for empty blocks -->
<module name="EmptyCatchBlock"> <!-- checks the catch blocks contain comment -->
<property name="commentFormat" value="expected|ignore"/>
</module>
<module name="EmptyForInitializerPad"/> <!-- empty for loop initializer must have no spaces -->
<module name="EmptyForIteratorPad"/> <!-- empty for loop iterator muse have have no spaces -->
<module name="EmptyLineSeparator"/> <!-- enforce blank lines after header, fields, constructors, methods, etc -->
<module name="EmptyStatement"/> <!-- prevent standalone ";" semicolons -->
<module name="EqualsHashCode"/> <!-- if equals() is overridden then so must hashCode() be -->
<module name="ExecutableStatementCount"/> <!-- limit executable statements to 30 per method -->
<module name="ExplicitInitialization"/> <!-- avoid initializing a field twice to the same value -->
<module name="FallThrough"/> <!-- checks that each switch case fall-through is commented as such -->
<module name="FinalClass"/> <!-- class which has only private constructors is declared as final -->
<!--<module name="FinalLocalVariable"/> &lt;!&ndash; incompatible with lombok's val - local variables that never change must be declared final &ndash;&gt;-->
<module name="FinalParameters"/> <!-- parameters that never change must be declared final -->
<module name="GenericWhitespace"/> <!-- generic tokens are spaced correctly -->
<module name="HiddenField">
<property name="ignoreConstructorParameter" value="true"/>
<property name="ignoreSetter" value="true"/>
</module>
<module name="HideUtilityClassConstructor"/> <!-- suppress for class with public static void main(...) -->
<module name="IllegalCatch"/> <!-- prevent generic catches (i.e. Exception, Throwable, RuntimeException) -->
<module name="IllegalImport"/> <!-- prevent imports from the sun.* package -->
<module name="IllegalThrows"/> <!-- prevent generic throws (i.e. Exception, Throwable, RuntimeException) -->
<module name="IllegalType"/> <!-- prevents variables, returns or parameters of non-interface Collections classes -->
<!--<module name="Indentation"/> &lt;!&ndash; incompatible with preferred indentation - correct indentation of Java code &ndash;&gt;-->
<module name="InnerAssignment"/> <!-- prevent assignments in subexpressions (i.e. while((line = read()){}) -->
<module name="InnerTypeLast"/> <!-- inner classes appear after methods and fields -->
<module name="InterfaceIsType"/> <!-- interface must define method not just constants -->
<module name="JavaNCSS"/> <!-- Non-Commenting Source Statements complexity analysis -->
<module name="JavadocMethod"> <!-- methods should have javadoc block -->
<property name="scope" value="public"/> <!-- if they are public -->
</module>
<module name="JavadocParagraph"/> <!-- javadoc paragraphs have opening <p> elements -->
<module name="JavadocStyle"/> <!-- javadoc comments are well formed -->
<module name="JavadocType"/> <!-- jacvadoc is present for classes, interfaces and enums -->
<module name="LeftCurly"/> <!-- placement of left curly braces ('{') for code blocks at end of line -->
<module name="LineLength"/> <!-- lines can't be longer the 80 -->
<module name="LocalFinalVariableName"/> <!-- validates identifiers for local, final variables, including catch parameters -->
<module name="LocalVariableName"/> <!-- validates non-final identifiers -->
<module name="MagicNumber"/> <!-- checks for magic numbers -->
<module name="MemberName"/> <!-- instance variable names conform to ^[a-z][a-zA-Z0-9]*$ -->
<module name="MethodCount"> <!-- restrict the number of methods in a class -->
<property name="maxTotal" value="30"/>
</module>
<module name="MethodLength"> <!-- restrict the number of lines in a method -->
<property name="max" value="60"/>
</module>
<module name="MethodName"/> <!-- method names conform to ^[a-z][a-zA-Z0-9]*$ -->
<module name="MethodParamPad"/> <!-- verifies padding around method parameters -->
<module name="MissingDeprecated"/> <!-- @Deprecated annotation must be accompanied by javadoc @deprecated -->
<module name="MissingSwitchDefault"/> <!-- switch must have a default -->
<module name="ModifiedControlVariable"/> <!-- prevent for loop control being modified inside loop -->
<module name="ModifierOrder"/> <!-- enforce order: public protected private abstract static final transient volatile synchronized native strictfp -->
<module name="MultipleStringLiterals"/> <!-- merge string literals -->
<module name="MultipleVariableDeclarations"/> <!-- declare variables separately -->
<module name="MutableException"/> <!-- prevent changing an exception once created -->
<module name="NPathComplexity"> <!-- restrict method complexity -->
<property name="max" value="400"/>
</module>
<module name="NeedBraces"/> <!-- braces around code blocks -->
<module name="NestedForDepth"/> <!-- prevent nested for loops -->
<module name="NestedIfDepth"/> <!-- prevent nested if-else blocks -->
<module name="NestedTryDepth"/> <!-- prevent nested try blocks -->
<module name="NoClone"/> <!-- prevent overriding Object.clone() -->
<module name="NonEmptyAtclauseDescription"/> <!-- javadoc tags have descriptions -->
<module name="NoWhitespaceAfter"/> <!-- prevent white space after tokens -->
<module name="NoWhitespaceBefore"/> <!-- prevent white space before tokens -->
<module name="OneStatementPerLine"/> <!-- only one statement per line -->
<module name="OneTopLevelClass"/> <!-- one top-level class per file -->
<module name="OperatorWrap"/> <!-- when line wrapping on an operator, the operator is on the new line -->
<module name="OuterTypeFilename"/> <!-- class name and filename must match -->
<module name="OverloadMethodsDeclarationOrder"/> <!-- group overloaded methods together -->
<module name="PackageDeclaration"/> <!-- class must have package and it must match the directory -->
<module name="PackageName"/> <!-- validate package name format -->
<module name="ParameterName"/> <!-- validate parameter name format -->
<module name="ParameterNumber"/> <!-- limits the number of parameters to 7 -->
<module name="ParenPad"/> <!-- parentheses should have no padding spaces -->
<module name="RedundantImport"/> <!-- checks for redundant imports (i.e. in the same package) -->
<module name="RedundantModifier"/> <!-- checks for redundant modifies (e.g. public methods in an interface) -->
<module name="ReturnCount"/> <!-- Restricts return statements to 2 per method (1 if return type is void) -->
<module name="RightCurly"/> <!-- placement of right curly braces ('}') for code blocks at end of line -->
<module name="SingleSpaceSeparator"/> <!-- Checks that non-whitespace characters are separated by only 1 character -->
<module name="SeparatorWrap"> <!-- checks line wrapping on separators (,) have separator at the end of the line -->
<property name="tokens" value="COMMA"/>
<property name="option" value="eol"/>
</module>
<module name="SeparatorWrap"> <!-- checks line wrapping on separators (.) have separator on the new line -->
<property name="tokens" value="DOT"/>
<property name="option" value="nl"/>
</module>
<module name="SimplifyBooleanExpression"/> <!-- finds code like if (b == true), b || true, !false, etc. -->
<module name="SimplifyBooleanReturn"/> <!-- overly complicated boolean return statements. -->
<module name="StaticVariableName"/> <!-- Static non-finals should be formatted like normal identifiers -->
<module name="StringLiteralEquality"/> <!-- use .equals(...) when comparing strings for equality -->
<module name="SuppressWarningsHolder"/> <!-- holds all @SuppressWarnings found for SuppressWarningsFilter -->
<module name="ThrowsCount"/> <!-- Restricts throws statements to 4 -->
<module name="TodoComment"> <!-- no to do or fix me comments -->
<property name="format" value="(TODO)|(FIXME)"/>
</module>
<module name="TrailingComment"/> <!-- no end-line comments (the irony!) -->
<module name="TypeName"/> <!-- validates class, interface, enum and annotation names -->
<module name="TypecastParenPad"/> <!-- no spaces in type casting parentheses -->
<module name="UnnecessaryParentheses"/> <!-- unnecessary parentheses -->
<module name="UnusedImports"/> <!-- checks for import that aren't used -->
<module name="UpperEll"/> <!-- long constants have an 'L' suffix -->
<module name="VariableDeclarationUsageDistance"/> <!-- distance between declaration of variable and first usage -->
<module name="VisibilityModifier"/> <!-- visibility of class members -->
<module name="WhitespaceAfter"/> <!-- comma, semicolon, and type cast are followed by a space -->
<module name="WhitespaceAround"/> <!-- selected token are surrounded by a space -->
<!-- Sevntu checks -->
<!-- sevntu/coding -->
<module name="AvoidConstantAsFirstOperandInConditionCheck"/> <!-- avoid code like if(12 == value){...} -->
<module name="AvoidHidingCauseExceptionCheck"/> <!-- ensure exceptions are propagated as cause in subsequent exceptions -->
<module name="AvoidNotShortCircuitOperatorsForBooleanCheck"/> <!-- limits using of not short-circuit operators -->
<module name="ConfusingConditionCheck"/> <!-- prevents negation within an "if" expression if "else" is present -->
<module name="DiamondOperatorForVariableDefinitionCheck"/> <!-- use the diamond operator -->
<module name="EitherLogOrThrowCheck"/> <!-- log or throw an exception - don't do both -->
<module name="ForbidCCommentsInMethodsCheck"/> <!-- prevent /* C-style */ comments inside methods -->
<module name="ForbidReturnInFinallyBlockCheck"/> <!-- returns in finally override returns elsewhere in method -->
<module name="ForbidThrowAnonymousExceptionsCheck"/> <!-- only throw concrete exceptions -->
<module name="LogicConditionNeedOptimizationCheck"/> <!-- prevent placement of local variables and fields after call to method in logical conditionals -->
<module name="MapIterationInForEachLoopCheck"/> <!-- warns of unoptimised map iteration -->
<module name="NameConventionForJunit4TestClassesCheck"/> <!-- checks names of test classes -->
<module name="NoNullForCollectionReturnCheck"/> <!-- don't return null when should be an empty collection -->
<module name="NumericLiteralNeedsUnderscoreCheck"/> <!-- long numeric literals have underscore spacers -->
<module name="OverridableMethodInConstructorCheck"/> <!-- don't call overridable method from constructor -->
<module name="RedundantReturnCheck"/> <!-- returns at end of void methods is pointless -->
<module name="ReturnBooleanFromTernaryCheck"/> <!-- use value inside condition -->
<module name="ReturnNullInsteadOfBooleanCheck"/> <!-- Boolean is NOT a ternary state object -->
<module name="SimpleAccessorNameNotationCheck"/> <!-- setters and getters must match to the field of the same name and type -->
<module name="SingleBreakOrContinueCheck"/> <!-- avoid complicated flow control -->
<module name="TernaryPerExpressionCountCheck"/> <!-- prevent nested ternary expressions -->
<module name="UselessSingleCatchCheck"/> <!-- prevent single catch blocks that just rethrow the original exception -->
<module name="UselessSuperCtorCallCheck"/> <!-- detects calls to super() when not needed -->
<!-- sevntu/design -->
<module name="AvoidConditionInversionCheck"/> <!-- catch condition inversion which could be more readable -->
<!--<module name="ChildBlockLengthCheck"/> &lt;!&ndash; broken in sevntu 1.20 - limit child blocks to 80% of parent block &ndash;&gt;-->
<module name="ConstructorWithoutParamsCheck"/> <!-- Exception classes must take parameters -->
<module name="ForbidWildcardAsReturnTypeCheck"/> <!-- forbid <? extends|super Object> generics as return types on public, protected and package methods -->
<module name="NestedSwitchCheck"/> <!-- prevent nested switch statements -->
<module name="NoMainMethodInAbstractClassCheck"/> <!-- Forbids main methods in abstract classes -->
<module name="PublicReferenceToPrivateTypeCheck"/> <!-- prevent attempt to expose private type -->
<!-- sevntu/naming -->
<module name="EnumValueNameCheck"/> <!-- validate enum value format -->
</module>
</module>

View file

@ -1,39 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project-shared-configuration>
<!--
This file contains additional configuration written by modules in the NetBeans IDE.
The configuration is intended to be shared among all the users of project and
therefore it is assumed to be part of version control checkout.
Without this configuration present, some functionality in the IDE may be limited or fail altogether.
-->
<properties xmlns="http://www.netbeans.org/ns/maven-properties-data/1">
<!--
Properties that influence various parts of the IDE, especially code formatting and the like.
You can copy and paste the single properties, into the pom.xml file and the IDE will pick them up.
That way multiple projects can share the same settings (useful for formatting rules for example).
Any value defined here will override the pom.xml file value but is only applicable to the current project.
-->
<netbeans.checkstyle.format>true</netbeans.checkstyle.format>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.importGroupsOrder>*;static *;java;javax;org.springframework;uk.ac.fife</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.importGroupsOrder>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.separateStaticImports>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.separateStaticImports>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.blankLinesAfterFields>1</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.blankLinesAfterFields>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.blankLineAfterJavadocReturnTag>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.blankLineAfterJavadocReturnTag>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignJavadocExceptionDescriptions>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignJavadocExceptionDescriptions>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.enableBlockCommentFormatting>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.enableBlockCommentFormatting>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignJavadocReturnDescription>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignJavadocReturnDescription>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.blankLineAfterJavadocParameterDescriptions>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.blankLineAfterJavadocParameterDescriptions>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignJavadocParameterDescriptions>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.alignJavadocParameterDescriptions>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.blankLinesBeforeFields>1</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.blankLinesBeforeFields>
<org-netbeans-modules-editor-indent.CodeStyle.project.text-line-wrap>none</org-netbeans-modules-editor-indent.CodeStyle.project.text-line-wrap>
<org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width>4</org-netbeans-modules-editor-indent.CodeStyle.project.indent-shift-width>
<org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab>4</org-netbeans-modules-editor-indent.CodeStyle.project.spaces-per-tab>
<org-netbeans-modules-editor-indent.CodeStyle.project.tab-size>8</org-netbeans-modules-editor-indent.CodeStyle.project.tab-size>
<org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width>80</org-netbeans-modules-editor-indent.CodeStyle.project.text-limit-width>
<org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs>true</org-netbeans-modules-editor-indent.CodeStyle.project.expand-tabs>
<org-netbeans-modules-editor-indent.CodeStyle.usedProfile>project</org-netbeans-modules-editor-indent.CodeStyle.usedProfile>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.sortMembersInGroups>true</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.sortMembersInGroups>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classMemberInsertionPoint>ORDERED_IN_CATEGORY</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.classMemberInsertionPoint>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.placeCatchOnNewLine>false</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.placeCatchOnNewLine>
<org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.placeFinallyOnNewLine>false</org-netbeans-modules-editor-indent.text.x-java.CodeStyle.project.placeFinallyOnNewLine>
</properties>
</project-shared-configuration>

View file

@ -49,9 +49,15 @@
<jacoco-class-line-covered-ratio>0</jacoco-class-line-covered-ratio> <jacoco-class-line-covered-ratio>0</jacoco-class-line-covered-ratio>
<jacoco-class-instruction-covered-ratio>0</jacoco-class-instruction-covered-ratio> <jacoco-class-instruction-covered-ratio>0</jacoco-class-instruction-covered-ratio>
<jacoco-class-missed-count-maximum>1</jacoco-class-missed-count-maximum> <jacoco-class-missed-count-maximum>1</jacoco-class-missed-count-maximum>
<mon.version>0.12.0</mon.version>
</properties> </properties>
<dependencies> <dependencies>
<dependency>
<groupId>net.kemitix</groupId>
<artifactId>mon</artifactId>
<version>${mon.version}</version>
</dependency>
<dependency> <dependency>
<groupId>javax.mail</groupId> <groupId>javax.mail</groupId>
<artifactId>mail</artifactId> <artifactId>mail</artifactId>

View file

@ -0,0 +1,39 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 Paul Campbell
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package net.kemitix.wiser.assertions;
/**
* Assertions caused by errors while parsing mime message content.
*
* @author Paul Campbell (pcampbell@kemitix.net)
*/
public class MimeMessageException extends RuntimeException {
/**
* Create an assertion with a message.
*
* @param message the message
*/
public MimeMessageException(final String message) {
super(message);
}
}

View file

@ -0,0 +1,40 @@
/**
* The MIT License (MIT)
*
* Copyright (c) 2018 Paul Campbell
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software
* and associated documentation files (the "Software"), to deal in the Software without restriction,
* including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
* subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies
* or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
* DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package net.kemitix.wiser.assertions;
/**
* Assertions caused by {@link WiserAssertions}.
*
* @author Paul Campbell (pcampbell@kemitix.net)
*/
public class WiserAssertionException extends RuntimeException {
/**
* Create a wrapper for the cause.
*
* @param cause the original exception
*/
public WiserAssertionException(final Throwable cause) {
super(cause);
}
}

View file

@ -21,12 +21,14 @@
package net.kemitix.wiser.assertions; package net.kemitix.wiser.assertions;
import net.kemitix.mon.maybe.Maybe;
import org.subethamail.wiser.Wiser; import org.subethamail.wiser.Wiser;
import org.subethamail.wiser.WiserMessage; import org.subethamail.wiser.WiserMessage;
import java.io.IOException; import java.io.IOException;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.function.Predicate; import java.util.function.Predicate;
import java.util.function.Supplier; import java.util.function.Supplier;
@ -71,8 +73,10 @@ import javax.mail.internet.MimeMultipart;
@SuppressWarnings("methodcount") @SuppressWarnings("methodcount")
public final class WiserAssertions { public final class WiserAssertions {
private static final String ERROR_MESSAGE_SUBJECT private static final String ERROR_MESSAGE_SUBJECT = "No message with subject [{0}] found!";
= "No message with subject [{0}] found!"; private static final String ERROR_MESSAGE_CONTENT_CONTAINS = "No message with content containing [{0}] found!";
private static final String ERROR_MESSAGE_CONTENT = "No message with content [{0}] found!";
private static final String ERROR_MESSAGE_TO = "No message to [{0}] found!";
/** /**
* The messages received by Wiser. * The messages received by Wiser.
@ -109,25 +113,15 @@ public final class WiserAssertions {
* @return the {@code WiserAssertions} instance * @return the {@code WiserAssertions} instance
*/ */
public WiserAssertions from(final String sender) { public WiserAssertions from(final String sender) {
findFirstOrElseThrow(m -> m.getEnvelopeSender().equals(sender), messageMatches(m -> m.getEnvelopeSender().equals(sender))
assertionError("No message from [{0}] found!", sender)); .orElseThrow(assertionError("No message from [{0}] found!", sender));
return this; return this;
} }
/** private Optional<WiserMessage> messageMatches(final Predicate<WiserMessage> predicate) {
* Checks that at least on message matches the predicate or the supplied return messages.stream()
* exception will be thrown.
*
* @param predicate the condition a message must match
* @param exceptionSupplier the supplier of the exception
*/
private void findFirstOrElseThrow(
final Predicate<WiserMessage> predicate,
final Supplier<AssertionError> exceptionSupplier) {
messages.stream()
.filter(predicate) .filter(predicate)
.findFirst() .findAny();
.orElseThrow(exceptionSupplier);
} }
/** /**
@ -141,10 +135,8 @@ public final class WiserAssertions {
*/ */
@SuppressWarnings( @SuppressWarnings(
{"ThrowableInstanceNotThrown", "ThrowableInstanceNeverThrown"}) {"ThrowableInstanceNotThrown", "ThrowableInstanceNeverThrown"})
private static Supplier<AssertionError> assertionError( private static Supplier<AssertionError> assertionError(final String errorMessage, final Object... args) {
final String errorMessage, final Object... args) { return () -> new AssertionError(MessageFormat.format(errorMessage, args));
return () -> new AssertionError(
MessageFormat.format(errorMessage, args));
} }
/** /**
@ -156,8 +148,8 @@ public final class WiserAssertions {
* @return the {@code WiserAssertions} instance * @return the {@code WiserAssertions} instance
*/ */
public WiserAssertions to(final String recipient) { public WiserAssertions to(final String recipient) {
findFirstOrElseThrow(m -> m.getEnvelopeReceiver().equals(recipient), messageMatches(m -> m.getEnvelopeReceiver().equals(recipient))
assertionError("No message to [{0}] found!", recipient)); .orElseThrow(assertionError(ERROR_MESSAGE_TO, recipient));
return this; return this;
} }
@ -170,44 +162,19 @@ public final class WiserAssertions {
* @return the {@code WiserAssertions} instance * @return the {@code WiserAssertions} instance
*/ */
public WiserAssertions withSubject(final String subject) { public WiserAssertions withSubject(final String subject) {
Predicate<WiserMessage> predicate = m -> subject.equals( messageMatches(m -> subject(m).equals(subject))
unchecked(getMimeMessage(m)::getSubject)); .orElseThrow(assertionError(ERROR_MESSAGE_SUBJECT, subject));
findFirstOrElseThrow(predicate,
assertionError(ERROR_MESSAGE_SUBJECT, subject));
return this; return this;
} }
/** private String subject(final WiserMessage wiserMessage) {
* Convert any checked Exceptions into unchecked Exceptions.
*
* @param <T> the item type to be returned after suppressing any
* checked exceptions
* @param supplier the source of the return value that could cause a checked
* exception
*
* @return the product of the supplier
*/
@SuppressWarnings("illegalCatch")
public static <T> T unchecked(final ThrowingSupplier<T> supplier) {
try { try {
return supplier.get(); return wiserMessage.getMimeMessage().getSubject();
} catch (Throwable e) { } catch (MessagingException e) {
throw new RuntimeException(e); throw new IllegalArgumentException("Invalid email message", e);
} }
} }
/**
* Returns the mime message within the {@link WiserMessage} converting any
* {@link MessagingException}s into {@link RuntimeException}s.
*
* @param wiserMessage the message
*
* @return the mime message
*/
private MimeMessage getMimeMessage(final WiserMessage wiserMessage) {
return unchecked(wiserMessage::getMimeMessage);
}
/** /**
* Checks that there was at least one email received that has a subject that * Checks that there was at least one email received that has a subject that
* contains the search text. * contains the search text.
@ -217,10 +184,8 @@ public final class WiserAssertions {
* @return the {@code WiserAssertions} instance * @return the {@code WiserAssertions} instance
*/ */
public WiserAssertions withSubjectContains(final String subject) { public WiserAssertions withSubjectContains(final String subject) {
Predicate<WiserMessage> predicate = m -> unchecked( messageMatches(m -> subject(m).contains(subject))
getMimeMessage(m)::getSubject).contains(subject); .orElseThrow(assertionError(ERROR_MESSAGE_SUBJECT, subject));
findFirstOrElseThrow(predicate,
assertionError(ERROR_MESSAGE_SUBJECT, subject));
return this; return this;
} }
@ -233,14 +198,51 @@ public final class WiserAssertions {
* @return the {@code WiserAssertions} instance * @return the {@code WiserAssertions} instance
*/ */
public WiserAssertions withContent(final String content) { public WiserAssertions withContent(final String content) {
findFirstOrElseThrow(m -> { messageMatches(m -> messageBody(m).trim().equals(content.trim()))
ThrowingSupplier<String> contentAsString = () -> getMimeMessageBody( .orElseThrow(assertionError(ERROR_MESSAGE_CONTENT, content));
m).trim();
return content.equals(unchecked(contentAsString));
}, assertionError("No message with content [{0}] found!", content));
return this; return this;
} }
private String messageBody(final WiserMessage m) {
try {
return messageBody(m.getMimeMessage().getContent());
} catch (IOException | MessagingException e) {
throw new RuntimeException(e);
}
}
private String messageBody(final Object content) {
return contentAsString(content)
.or(() -> contentAsMimeMessage(content))
.or(() -> contentAsMultiPartMime(content))
.orElseThrow(() -> new RuntimeException("Unexpected MimeMessage content"));
}
private Maybe<String> contentAsString(final Object content) {
if (content instanceof String) {
return Maybe.just((String) content);
}
return Maybe.nothing();
}
private Maybe<String> contentAsMimeMessage(final Object content) {
if (content instanceof MimeMessage) {
return Maybe.just(content.toString());
}
return Maybe.nothing();
}
private Maybe<String> contentAsMultiPartMime(final Object content) {
if (content instanceof MimeMultipart) {
try {
return Maybe.just(getMimeMultipartAsString((MimeMultipart) content));
} catch (MessagingException | IOException e) {
throw new RuntimeException(e);
}
}
return Maybe.nothing();
}
/** /**
* Check that there was at least one email received that contains the search * Check that there was at least one email received that contains the search
* text. * text.
@ -250,48 +252,11 @@ public final class WiserAssertions {
* @return the {@code WiserAssertions} instance * @return the {@code WiserAssertions} instance
*/ */
public WiserAssertions withContentContains(final String content) { public WiserAssertions withContentContains(final String content) {
StringBuilder messageContent = new StringBuilder(); messageMatches((WiserMessage m) -> messageBody(m).trim().contains(content))
findFirstOrElseThrow((WiserMessage m) -> { .orElseThrow(assertionError(ERROR_MESSAGE_CONTENT_CONTAINS, content));
ThrowingSupplier<String> contentAsString = () -> getMimeMessageBody(
m).trim();
messageContent.append(unchecked(contentAsString));
return unchecked(contentAsString).contains(content);
}, assertionError(
"No message with content containing [{0}] found! Was {1}",
content, messageContent));
return this; return this;
} }
/**
* Returns the body of the message.
*
* @param message the message
*
* @return the body of the message
*
* @throws IOException if error extracting the mime message
* @throws MessagingException if the message type is not known
*/
@SuppressWarnings("npathcomplexity")
private String getMimeMessageBody(final WiserMessage message)
throws IOException, MessagingException {
Object content = getMimeMessage(message).getContent();
String result = null;
if (content instanceof String) {
result = (String) content;
}
if (content instanceof MimeMessage) {
result = content.toString();
}
if (content instanceof MimeMultipart) {
result = getMimeMultipartAsString((MimeMultipart) content);
}
if (result == null) {
throw new RuntimeException("Unexpected MimeMessage content");
}
return result;
}
/** /**
* Converts a {@link MimeMultipart} into a {@link String} stripping out the * Converts a {@link MimeMultipart} into a {@link String} stripping out the
* mime part boundary and headers.. * mime part boundary and headers..
@ -317,22 +282,4 @@ public final class WiserAssertions {
return sb.toString(); return sb.toString();
} }
/**
* Interface for providing a value that could thrown an exception when
* sought.
*
* @param <T> the type of value to be supplied
*/
public interface ThrowingSupplier<T> {
/**
* Returns the value.
*
* @return the value
*
* @throws Throwable on error
*/
@SuppressWarnings("illegalthrows")
T get() throws Throwable;
}
} }

View file

@ -41,7 +41,7 @@ abstract class AbstractWiserTest {
* *
* @return the wiser assertions * @return the wiser assertions
*/ */
protected WiserAssertions getAssertions() { protected WiserAssertions assertReceivedMessage() {
return WiserAssertions.assertReceivedMessage(wiser); return WiserAssertions.assertReceivedMessage(wiser);
} }

View file

@ -31,7 +31,7 @@ public class Issue1Test extends AbstractWiserTest {
//when //when
mailer.sendMail(email); mailer.sendMail(email);
//then //then
getAssertions().withContentContains("Hi Carl"); assertReceivedMessage().withContentContains("Hi Carl");
} }
} }

View file

@ -30,9 +30,9 @@ public class Issue6Test extends AbstractWiserTest {
//when //when
sender.send(message); sender.send(message);
//then //then
getAssertions().from("bob@a.com").to("carl@b.com") assertReceivedMessage().from("bob@a.com").to("carl@b.com")
.withSubject("Subject") .withSubject("Subject")
.withContentContains("Hi Carl"); .withContentContains("Hi Carl");
} }
} }

View file

@ -68,7 +68,7 @@ public class WiserAssertionsTest extends AbstractWiserTest {
//when //when
sendMimeMultipartMessage("from", "to", "subject", body); sendMimeMultipartMessage("from", "to", "subject", body);
//then //then
getAssertions().withContent(body); assertReceivedMessage().withContent(body);
} }
/** /**
@ -82,7 +82,7 @@ public class WiserAssertionsTest extends AbstractWiserTest {
//when //when
sendMimeMultipartMessage("from", "to", "subject", body); sendMimeMultipartMessage("from", "to", "subject", body);
//then //then
getAssertions().withContent("Other body"); assertReceivedMessage().withContent("Other body");
} }
/** /**
@ -96,7 +96,7 @@ public class WiserAssertionsTest extends AbstractWiserTest {
//when //when
sendMimeMultipartMessage("from", "to", "subject", body); sendMimeMultipartMessage("from", "to", "subject", body);
//then //then
getAssertions().withContentContains("age bo"); assertReceivedMessage().withContentContains("age bo");
} }
/** /**
@ -110,7 +110,7 @@ public class WiserAssertionsTest extends AbstractWiserTest {
//when //when
sendMimeMultipartMessage("from", "to", "subject", body); sendMimeMultipartMessage("from", "to", "subject", body);
//then //then
getAssertions().withContentContains("agebo"); assertReceivedMessage().withContentContains("agebo");
} }
/** /**
@ -126,7 +126,7 @@ public class WiserAssertionsTest extends AbstractWiserTest {
//when //when
sendMimeMultipartMessage(from, "to", "subject", "body"); sendMimeMultipartMessage(from, "to", "subject", "body");
//then //then
getAssertions().from(from); assertReceivedMessage().from(from);
} }
/** /**
@ -140,7 +140,7 @@ public class WiserAssertionsTest extends AbstractWiserTest {
//when //when
sendMimeMultipartMessage(from, "to", "subject", "body"); sendMimeMultipartMessage(from, "to", "subject", "body");
//then //then
getAssertions().from("lisa@c.com"); assertReceivedMessage().from("lisa@c.com");
} }
/** /**
@ -149,7 +149,7 @@ public class WiserAssertionsTest extends AbstractWiserTest {
*/ */
@Test @Test
public void testInstantiate() { public void testInstantiate() {
assertNotNull(getAssertions()); assertNotNull(assertReceivedMessage());
} }
/** /**
@ -164,7 +164,7 @@ public class WiserAssertionsTest extends AbstractWiserTest {
sendMimeMultipartMessage( sendMimeMultipartMessage(
"from", "to", "subject " + fragment + " tail", "body"); "from", "to", "subject " + fragment + " tail", "body");
//then //then
getAssertions().withSubjectContains(fragment); assertReceivedMessage().withSubjectContains(fragment);
} }
/** /**
@ -178,7 +178,7 @@ public class WiserAssertionsTest extends AbstractWiserTest {
//when //when
sendMimeMultipartMessage("from", "to", "subject tail", "body"); sendMimeMultipartMessage("from", "to", "subject tail", "body");
//then //then
getAssertions().withSubjectContains(fragment); assertReceivedMessage().withSubjectContains(fragment);
} }
/** /**
@ -192,7 +192,7 @@ public class WiserAssertionsTest extends AbstractWiserTest {
//when //when
sendMimeMultipartMessage("from", "to", subject, "body"); sendMimeMultipartMessage("from", "to", subject, "body");
//then //then
getAssertions().withSubject(subject); assertReceivedMessage().withSubject(subject);
} }
/** /**
@ -206,7 +206,7 @@ public class WiserAssertionsTest extends AbstractWiserTest {
//when //when
sendMimeMultipartMessage("from", "to", subject, "body"); sendMimeMultipartMessage("from", "to", subject, "body");
//then //then
getAssertions().withSubject("other subject"); assertReceivedMessage().withSubject("other subject");
} }
/** /**
@ -220,7 +220,7 @@ public class WiserAssertionsTest extends AbstractWiserTest {
//when //when
sendMimeMultipartMessage("from", to, "subject", "body"); sendMimeMultipartMessage("from", to, "subject", "body");
//then //then
getAssertions().to(to); assertReceivedMessage().to(to);
} }
/** /**
@ -235,7 +235,7 @@ public class WiserAssertionsTest extends AbstractWiserTest {
//when //when
sendMimeMultipartMessage("from", to, "subject", "body"); sendMimeMultipartMessage("from", to, "subject", "body");
//then //then
getAssertions().to("bob@a.com"); assertReceivedMessage().to("bob@a.com");
} }
} }