diff --git a/matchers/HEADER.txt b/matchers/HEADER.txt new file mode 100644 index 0000000..bd5cf4f --- /dev/null +++ b/matchers/HEADER.txt @@ -0,0 +1,14 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ diff --git a/matchers/LICENSE b/matchers/LICENSE new file mode 100644 index 0000000..883ab09 --- /dev/null +++ b/matchers/LICENSE @@ -0,0 +1,264 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +APACHE JACKRABBIT SUBCOMPONENTS + +Apache Jackrabbit includes parts with separate copyright notices and license +terms. Your use of these subcomponents is subject to the terms and conditions +of the following licenses: + + XPath 2.0/XQuery 1.0 Parser: + http://www.w3.org/2002/11/xquery-xpath-applets/xgrammar.zip + + Copyright (C) 2002 World Wide Web Consortium, (Massachusetts Institute of + Technology, European Research Consortium for Informatics and Mathematics, + Keio University). All Rights Reserved. + + This work is distributed under the W3C(R) Software License in the hope + that it will be useful, but WITHOUT ANY WARRANTY; without even the + implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + W3C(R) SOFTWARE NOTICE AND LICENSE + http://www.w3.org/Consortium/Legal/2002/copyright-software-20021231 + + This work (and included software, documentation such as READMEs, or + other related items) is being provided by the copyright holders under + the following license. By obtaining, using and/or copying this work, + you (the licensee) agree that you have read, understood, and will comply + with the following terms and conditions. + + Permission to copy, modify, and distribute this software and its + documentation, with or without modification, for any purpose and + without fee or royalty is hereby granted, provided that you include + the following on ALL copies of the software and documentation or + portions thereof, including modifications: + + 1. The full text of this NOTICE in a location viewable to users + of the redistributed or derivative work. + + 2. Any pre-existing intellectual property disclaimers, notices, + or terms and conditions. If none exist, the W3C Software Short + Notice should be included (hypertext is preferred, text is + permitted) within the body of any redistributed or derivative code. + + 3. Notice of any changes or modifications to the files, including + the date changes were made. (We recommend you provide URIs to the + location from which the code is derived.) + + THIS SOFTWARE AND DOCUMENTATION IS PROVIDED "AS IS," AND COPYRIGHT + HOLDERS MAKE NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, + INCLUDING BUT NOT LIMITED TO, WARRANTIES OF MERCHANTABILITY OR FITNESS + FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF THE SOFTWARE OR + DOCUMENTATION WILL NOT INFRINGE ANY THIRD PARTY PATENTS, COPYRIGHTS, + TRADEMARKS OR OTHER RIGHTS. + + COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL + OR CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR + DOCUMENTATION. + + The name and trademarks of copyright holders may NOT be used in + advertising or publicity pertaining to the software without specific, + written prior permission. Title to copyright in this software and + any associated documentation will at all times remain with + copyright holders. diff --git a/matchers/pom.xml b/matchers/pom.xml new file mode 100644 index 0000000..420d940 --- /dev/null +++ b/matchers/pom.xml @@ -0,0 +1,101 @@ + + + + + + 4.0.0 + + org.junit.contrib + junit-matchers + 0.1-SNAPSHOT + jar + + JUnit Matchers + Extended Hamcrest Matchers in the JUnit framework + 2011 + + + + Apache License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + + Tibor17 + Tibor Digana + + developer + + + + + + UTF-8 + UTF-8 + UTF-8 + + + + + junit + junit + 4.10 + + + + + + + ${basedir} + META-INF + + LICENSE + + + + + + maven-compiler-plugin + 2.0.2 + + 1.5 + 1.5 + + + + maven-javadoc-plugin + 2.7 + + + javadoc-aggregate + site + + aggregate + + + + + + + + diff --git a/matchers/src/main/java/org/junit/contrib/matchers/AbstractCallable.java b/matchers/src/main/java/org/junit/contrib/matchers/AbstractCallable.java new file mode 100644 index 0000000..129ca84 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/AbstractCallable.java @@ -0,0 +1,62 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +/** + * A skeleton class for {@link Block} and {@link Callable}. + *

+ * @author tibor17 + * @version 0.1 + * @see org.junit.contrib.matchers.IsThrowing + * @since 0.1, 26.2.2012, 20:37 + */ +public abstract class AbstractCallable { + private final boolean hasReturnValue; + private V returned; + + final V fallback; + boolean isSucceeded; + Throwable throwable; + + AbstractCallable(boolean hasReturnValue) { + this.hasReturnValue = hasReturnValue; + fallback = null; + } + + AbstractCallable(boolean hasReturnValue, V fallback) { + this.hasReturnValue = hasReturnValue; + this.fallback = fallback; + } + + abstract V call1() throws Exception; + + final V evaluate() { + isSucceeded = true; + try { + returned = call1(); + return returned; + } catch (final Throwable throwable) { + isSucceeded = false; + this.throwable = throwable; + return fallback; + } + } + + @Override final public String toString() { + return isSucceeded + ? ("block returned " + (hasReturnValue ? (returned == null ? "null" : returned.toString()) : "normally")) + : ("block threw " + throwable.toString()); + } +} \ No newline at end of file diff --git a/matchers/src/main/java/org/junit/contrib/matchers/Block.java b/matchers/src/main/java/org/junit/contrib/matchers/Block.java new file mode 100644 index 0000000..d122da5 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/Block.java @@ -0,0 +1,44 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +/** + * The skeleton class where the {@link #run()} must be implemented. + *

+ * @author tibor17 + * @version 0.1 + * @see org.junit.contrib.matchers.IsThrowing + * @since 0.1, 26.2.2012, 20:38 + */ +public abstract class Block extends AbstractCallable { + Block(boolean hasReturnValue, V fallback) { + super(hasReturnValue, fallback); + } + + Block(boolean hasReturnValue) { + super(hasReturnValue); + } + + public Block() { + super(false); + } + + protected abstract void run() throws Exception; + + @Override V call1() throws Exception { + run(); + return null; + } +} \ No newline at end of file diff --git a/matchers/src/main/java/org/junit/contrib/matchers/Callable.java b/matchers/src/main/java/org/junit/contrib/matchers/Callable.java new file mode 100644 index 0000000..53833a8 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/Callable.java @@ -0,0 +1,37 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +/** + * The skeleton class where the {@link #call()} must be implemented. + *

+ * @author tibor17 + * @version 0.1 + * @see org.junit.contrib.matchers.IsThrowing + * @since 0.1, 26.2.2012, 20:39 + */ +public abstract class Callable extends Block implements java.util.concurrent.Callable { + public Callable() { + super(true); + } + + public Callable(V fallback) { + super(true, fallback); + } + + @Override final V call1() throws Exception { + return call(); + } +} \ No newline at end of file diff --git a/matchers/src/main/java/org/junit/contrib/matchers/EndPosition.java b/matchers/src/main/java/org/junit/contrib/matchers/EndPosition.java new file mode 100644 index 0000000..b573262 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/EndPosition.java @@ -0,0 +1,45 @@ +package org.junit.contrib.matchers; + +import org.hamcrest.Matcher; + +/** + * Determines whether the {@link java.util.regex.Matcher} has end position matching + * the regex criteria by {@link #end(java.util.regex.Matcher)}. In prior the caller + * must satisfy the call {@link java.util.regex.Matcher#find(int)}. + *

+ * If a failure appears after calling {@link #end(java.util.regex.Matcher)}, then the + * method {@link #hasFailure()} returns {@code true} and the failure description is + * retrieved by the return from {@link #getFailureDescription()}. + */ +final class EndPosition extends Position { + private int end = -1; + private boolean matches, isEvaluated; + + @Override + protected boolean isEvaluated() { + return isEvaluated; + } + + EndPosition(int group, Matcher expectedIndex) { + super(group, expectedIndex); + } + + boolean end(final java.util.regex.Matcher regexMatcher) { + isEvaluated = true; + try { + end = regexMatcher.end(group); + matches = end != -1 && matchExpectedIndex(end); + } catch (IllegalStateException e) { + matches = false; + } + return matches; + } + + boolean hasFailure() { + return !matches && getFailureDescription() != null; + } + + @Override public String toString() { + return end == -1 ? "" : "(per group " + group + ") to exclusive end position " + end; + } +} \ No newline at end of file diff --git a/matchers/src/main/java/org/junit/contrib/matchers/IEndMatcher.java b/matchers/src/main/java/org/junit/contrib/matchers/IEndMatcher.java new file mode 100644 index 0000000..46ce354 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/IEndMatcher.java @@ -0,0 +1,84 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +import org.hamcrest.Matcher; + +/** + * Specifies the end position of regular expression matches. + * @author tibor17 + * @version 0.1 + * @see org.junit.contrib.matchers.IsRegex + * @since 0.1, 25.12.2011, 1:41 + */ +public interface IEndMatcher extends Matcher { + /** + * Matches the end index of the subsequence captured by the given group during the previous + * match or like operation. + *

+ * The integer matcher represents expected range of position. The first method parameter + * specifies a group of given regular expression to search matching end position. If given + * group is negative, then the {@link AssertionError} is thrown. + * @param group a group in regular expression + * @param index expected range of end position (exclusive) + * @throws AssertionError + * if given group is negative, + * index is null + * ; or if not applicable when result matcher already specified + * @return opposite matcher + */ + IStartMatcher endsAt(int group, Matcher index); + + /** + * Matches the end index of the subsequence captured during the previous match + * or like operation. + *

+ * The integer matcher represents expected range of end position with zero group. + * @param index expected range of end position (exclusive) + * @return opposite matcher + * @throws AssertionError + * index is null + * or; if not applicable when result matcher already specified + */ + IStartMatcher endsAt(Matcher index); + + /** + * Matches the end index of the subsequence, captured by the given group during the previous + * match or like operation, equal to the given index. + *

+ * The first method parameter specifies a group of given regular expression to search + * matching end position. If given group is negative, then the + * {@link AssertionError} is thrown. + * @param group a group in regular expression + * @param index expected range of end position (exclusive) + * @throws AssertionError + * if given group is negative + * ; or if not applicable when result matcher already specified + * @return opposite matcher + */ + IStartMatcher endsAt(int group, int index); + + /** + * Matches the end index of the subsequence, captured during the previous + * match or like operation, equal to the given index. + *

+ * The integer matcher represents expected range of end position with zero group. + * @param index expected range of end position (exclusive) + * @return opposite matcher + * @throws AssertionError + * if not applicable when result matcher already specified + */ + IStartMatcher endsAt(int index); +} diff --git a/matchers/src/main/java/org/junit/contrib/matchers/IIsThrowingMessage.java b/matchers/src/main/java/org/junit/contrib/matchers/IIsThrowingMessage.java new file mode 100644 index 0000000..5d5aab0 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/IIsThrowingMessage.java @@ -0,0 +1,32 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +import org.hamcrest.Matcher; + +/** + * This interface specifies post operations applied after successful matcher {@link IsThrowing}. + *

+ * + * @author tibor17 + * @version 0.1 + * @see org.junit.contrib.matchers.IsThrowing + * @since 0.1, 19.2.2012, 0:04 + */ +public interface IIsThrowingMessage> extends Matcher { + Matcher withMessage(String expectedMessage); + Matcher andMessage(Matcher matchWith); + Matcher andMessage(IsRegex regex); +} diff --git a/matchers/src/main/java/org/junit/contrib/matchers/IStartMatcher.java b/matchers/src/main/java/org/junit/contrib/matchers/IStartMatcher.java new file mode 100644 index 0000000..c4b1dfe --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/IStartMatcher.java @@ -0,0 +1,85 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +import org.hamcrest.Matcher; + +/** + * Specifies the start position of regular expression matches. + * @author tibor17 + * @version 0.1 + * @see org.junit.contrib.matchers.IsRegex + * @since 0.1, 25.12.2011, 1:35 + */ +public interface IStartMatcher extends Matcher { + /** + * Matches the start index of the subsequence captured by the given group during the previous + * match or like operation. + *

+ * The integer matcher represents expected range of position. The first method parameter + * specifies a group of given regular expression to search matching start position. If given + * group is negative, then the {@link AssertionError} is thrown. + * @param group a group in regular expression + * @param index expected range of start position (inclusive) + * @throws AssertionError + * if given group is negative, + * index is null + * ; or if not applicable when result matcher already specified + * @return opposite matcher + * @see java.util.regex.Matcher#start(int) + */ + IEndMatcher startsAt(int group, Matcher index); + + /** + * Matches the start index of the subsequence captured during the previous + * match or like operation. + *

+ * The integer matcher represents expected range of start position with zero group. + * @param index expected range of start position (inclusive) + * @return opposite matcher + * @throws AssertionError + * index is null + * ; or if not applicable when result matcher already specified + */ + IEndMatcher startsAt(Matcher index); + + /** + * Matches the start index of the subsequence, captured by the given group during the previous + * match or like operation, equal to the given index. + *

+ * The first method parameter specifies a group of given regular expression to search matching + * start position. If given group is negative, then the {@link AssertionError} is thrown. + * @param group a group in regular expression + * @param index expected range of start position (inclusive) + * @throws AssertionError + * if given group is negative + * ; or if not applicable when result matcher already specified + * @return opposite matcher + * @see java.util.regex.Matcher#start(int) + */ + IEndMatcher startsAt(int group, int index); + + /** + * Matches the start index of the subsequence, captured during the previous + * match or like operation, equal to the given index. + *

+ * The integer matcher represents expected range of start position with zero group. + * @param index expected range of start position (inclusive) + * @return opposite matcher + * @throws AssertionError + * if not applicable when result matcher already specified + */ + IEndMatcher startsAt(int index); +} diff --git a/matchers/src/main/java/org/junit/contrib/matchers/IThrownCallable.java b/matchers/src/main/java/org/junit/contrib/matchers/IThrownCallable.java new file mode 100644 index 0000000..98c0608 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/IThrownCallable.java @@ -0,0 +1,44 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +/** + * This interface specifies post operations after examined block returned. + *

+ * + * @author tibor17 + * @version 0.1 + * @see org.junit.contrib.matchers.IsThrowing + * @since 0.1, 19.2.2012, 21:45 + */ +public interface IThrownCallable { + /** + * Evaluates whether the block returned executed without throwing any exception. + * @return {@code true} if block executed without throwing any exception + */ + boolean isBlockReturnedNormally(); + + /** + * See {@link org.junit.contrib.matchers.Callable}. + * @return a value returned by a callable block, or a fallback when callable block threw + */ + V blockReturned(); + + /** + * See the parameter in {@linkplain org.junit.contrib.matchers.Callable constructor of Callable}. + * @return a fallback of a callable block which returns + */ + V fallback(); +} diff --git a/matchers/src/main/java/org/junit/contrib/matchers/InternalDescription.java b/matchers/src/main/java/org/junit/contrib/matchers/InternalDescription.java new file mode 100644 index 0000000..5f51426 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/InternalDescription.java @@ -0,0 +1,36 @@ +package org.junit.contrib.matchers; + +import org.hamcrest.BaseDescription; + +/** + * The purpose of this class is to handle description string + * by use of {@link org.hamcrest.Matcher#describeTo(org.hamcrest.Description)} + * as follows: + *

+ *      org.hamcrest.Matcher m = ...;
+ *      InternalDescription d = new InternalDescription();
+ *      m.describeTo(d);
+ *      String failureDescription = d.toString();
+ *      // use of failureDescription
+ *  
+ *

+ */ +final class InternalDescription extends BaseDescription { + private final StringBuilder msg; + + public InternalDescription() { + msg = new StringBuilder(128); + } + + public InternalDescription(String description) { + msg = new StringBuilder(description); + } + + @Override protected void append(char c) { + msg.append(c); + } + + @Override public String toString() { + return msg.toString(); + } +} \ No newline at end of file diff --git a/matchers/src/main/java/org/junit/contrib/matchers/IsAssignableTo.java b/matchers/src/main/java/org/junit/contrib/matchers/IsAssignableTo.java new file mode 100644 index 0000000..556220c --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/IsAssignableTo.java @@ -0,0 +1,61 @@ +package org.junit.contrib.matchers; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.internal.matchers.TypeSafeMatcher; + +import static org.junit.Assert.assertNotNull; + +/** + * Matches the actual reference type with the given type. + *

+ * If the actual type is same with or a subtype, in other words assignable, to the given expected type in the + * methods {@link #assignableTo(Class)}, {@link #assignableToAny(Class)}, {@link #assignableToThrowable(Class)}. + *

+ * The method {@link #assignableTo(Class)} uses same generic type on the returned value and method parameter. + *

+ * The method {@link #assignableToAny(Class)} allows to use wildcards Class. + *

+ * The last method {@link #assignableToThrowable(Class)} + * is suitable in use of exceptions, like it is in the {@linkplain IsThrowing IsThrowing matcher}. + *

+ * @param an actual type + * @param an expected type + */ +public class IsAssignableTo, E extends Class> extends TypeSafeMatcher { + private final E superOrSameType; + + public IsAssignableTo(final E superOrSameType) { + assertNotNull("expected 'superOrSameType' must not be null", superOrSameType); + this.superOrSameType = superOrSameType; + } + + @Override + public boolean matchesSafely(final A subOrSameType) { + return superOrSameType.isAssignableFrom(subOrSameType); + } + + @Override + public void describeTo(final Description description) { + description.appendText("assignable to ") + .appendText(superOrSameType.getName()); + } + + @org.hamcrest.Factory + public static Matcher> assignableTo(final Class superOrSameType) { + assertNotNull("superOrSameType is null", superOrSameType); + return new IsAssignableTo, Class>(superOrSameType); + } + + @org.hamcrest.Factory + public static Matcher> assignableToAny(final Class superOrSameType) { + assertNotNull("superOrSameType is null", superOrSameType); + return new IsAssignableTo, Class>(superOrSameType); + } + + @org.hamcrest.Factory + public static Matcher> assignableToThrowable(final Class superOrSameType) { + assertNotNull("superOrSameType is null", superOrSameType); + return new IsAssignableTo, Class>(superOrSameType); + } +} diff --git a/matchers/src/main/java/org/junit/contrib/matchers/IsRegex.java b/matchers/src/main/java/org/junit/contrib/matchers/IsRegex.java new file mode 100644 index 0000000..e864910 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/IsRegex.java @@ -0,0 +1,554 @@ +package org.junit.contrib.matchers; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.internal.matchers.TypeSafeMatcher; + +import java.util.HashSet; +import java.util.TreeSet; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import static java.util.regex.Pattern.compile; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; + +/** + * Matches regular expression with an input sequence specified by the actual value {@link T}. + *

+ * The matcher can be extended by specifying start and the end position found first by a + * positive result of the matcher. As for instance + *

+ *      assertThat("Hi There!", is(like("hi|hello", new RegexProperties(CASE_INSENSITIVE)).startsAt(equalTo(0))));
+ *      assertThat("Hi There!", is(like("Hi|Hello", region(0, 2))));
+ * 
+ *

+ * Both operations {@code startsAt} and {@code endsAt} have two alternatives. The integer + * matcher in last method parameter represents expected range of position. In other alternative + * {@link #startsAt(int, org.hamcrest.Matcher)} and {@link #endsAt(int, org.hamcrest.Matcher)} + * specifies a group of given regular expression. If this group is negative (i.e. not recognized within + * regular expression's group counter), then the the methods fail by throwing {@link AssertionError}. + * If the group, start, or end position is negative in + * {@link #startsAt(int)}, {@link #startsAt(int, int)}, {@link #startsAt(int, org.hamcrest.Matcher)}, + * {@link #endsAt(int)}, {@link #endsAt(int, int)}, {@link #endsAt(int, org.hamcrest.Matcher)}. + * If the methods {@link #startsAt(org.hamcrest.Matcher)}, {@link #startsAt(int, org.hamcrest.Matcher)}, + * {@link #endsAt(org.hamcrest.Matcher)}, {@link #endsAt(int, org.hamcrest.Matcher)} are used with + * null in reference parameter, the method call fails by throwing {@link AssertionError}. + * @param an actual input sequence + */ +public final class IsRegex + extends TypeSafeMatcher implements IStartMatcher, IEndMatcher { + private static enum MatchOperation { + MATCH, FIND + } + + private final Pattern pattern; + private final Matcher> successfulResultMatcher; + private final RegexProperties regexProperties; + private final MatchOperation matchOperation; + + private java.util.regex.Matcher matcher; + private StartPosition start; + private EndPosition end; + private String majorFault; + + /** + * Compiles the given regular expression into a pattern with the given flags. + *

+ * @param regex + * The expression to be compiled. + * @param successfulResultMatcher + * Hamcrest matcher applied after successful match of input subsequence with the given regex. + * @param matchOperation + * Specifies {@link java.util.regex.Matcher#matches()} or {@link java.util.regex.Matcher#find(int)} by + * {@link MatchOperation#MATCH} or {@link MatchOperation#FIND}, respectively. + * @param regexProperties + * input sequence properties, and properties of this matcher of regular expression + * @throws AssertionError (a unit failure) + * If regex, matchOperation or regexProperties is null + * ; or if the expression's syntax is invalid. + */ + public IsRegex(final String regex, + final Matcher> successfulResultMatcher, + final MatchOperation matchOperation, + final RegexProperties regexProperties) { + + assertNotNull("regular expression must not be null", regex); + assertNotNull("match operation must not be null", matchOperation); + assertNotNull("properties of regular expression must not be null", regexProperties); + + int patternFlags = 0; + for (final MatchFlag patternFlag : regexProperties.getFlags()) + patternFlags |= patternFlag.getJavaFlag(); + + Pattern pattern = null; + try { + pattern = compile(regex, patternFlags); + } catch (PatternSyntaxException e) { + fail(e.getMessage()); + } finally { + this.pattern = pattern; + } + this.successfulResultMatcher = successfulResultMatcher; + this.matchOperation = matchOperation; + this.regexProperties = regexProperties; + } + + /** + * + * @param input input sequence + * @return {@code true} if matched + * @throws AssertionError + * if input is null; + * or if start or end position in {@link RegexProperties} is greater than input sequence length; + * or if group in startsAt or endsAt is greater than group counter. + */ + @Override + public boolean matchesSafely(final T input) { + majorFault = null; + + if (input == null) { + majorFault = "input sequence is null"; + fail(majorFault); + } + + final int startPosition = regexProperties.getStartRegion(), + endPosition = regexProperties.getEndRegion(), + inputLength = input.length(); + + if (startPosition > inputLength) { + majorFault = "the start position (" + + startPosition + + ") is greater than the given input sequence length " + + inputLength; + fail(majorFault); + } + + if (endPosition > inputLength) { + majorFault = "the end position (" + + endPosition + + ") is greater than the given input sequence length " + + inputLength; + fail(majorFault); + } + + matcher = startPosition == 0 && endPosition == -1 ? + pattern.matcher(input) + : pattern.matcher(input).region(startPosition, endPosition == -1 ? inputLength : endPosition); + + if (!regexProperties.hasAnchoringBounds()) matcher.useAnchoringBounds(false); + if (regexProperties.hasTransparentBounds()) matcher.useTransparentBounds(true); + + final HashSet regexResults = new HashSet(); + + if (successfulResultMatcher == null) { + if (start != null && start.group > matcher.groupCount()) { + majorFault = "group " + start.group + " must not be greater than " + matcher.groupCount(); + fail(majorFault); + } + + if (end != null && end.group > matcher.groupCount()) { + majorFault = "group " + end.group + " must not be greater than " + matcher.groupCount(); + fail(majorFault); + } + + if (matchOperation == MatchOperation.MATCH) + return matcher.matches() + && ((start == null || start.start(matcher)) + & (end == null || end.end(matcher))); + else { + populateResults(regexResults); + return !regexResults.isEmpty() + && ((start == null || matchesStart(start, regexResults)) + & (end == null || matchesEnd(end, regexResults))); + } + } else { + if (matchOperation == MatchOperation.FIND + || matcher.matches()) + populateResults(regexResults); + + return !regexResults.isEmpty() + && successfulResultMatcher.matches(regexResults); + } + } + + private boolean matchesStart(final StartPosition expected, final HashSet regexResults) { + for (final RegexResult regexResult : regexResults) { + if (expected.group == regexResult.getGroup() + && expected.expectedIndex.matches(regexResult.getStart())) + return true; + } + return false; + } + + private boolean matchesEnd(final EndPosition expected, final HashSet regexResults) { + for (final RegexResult regexResult : regexResults) { + if (expected.group == regexResult.getGroup() + && expected.expectedIndex.matches(regexResult.getEnd())) + return true; + } + return false; + } + + private void populateResults(final HashSet regexResults) { + for (int start, end; matcher.find(); ) { + start = matcher.start(); + end = matcher.end(); + if (start != -1 + && end != -1) + regexResults.add(new RegexResult(0, start, end)); + } + + if (!matcher.find(0)) return; + + for (int start, end, group = 0, groupCount = matcher.groupCount(); group <= groupCount; ++group) { + start = matcher.start(group); + end = matcher.end(group); + if (start != -1 + && end != -1) + regexResults.add(new RegexResult(group, start, end)); + } + } + + private static String humanReadableFlags(MatchFlag[] flags) { + if (flags == null || flags.length == 0) return ""; + TreeSet sortedFlags = new TreeSet(); + for (final MatchFlag flag : flags) + if (flag != null && flag.getEmbeddedFlagExpression() != null) + sortedFlags.add(flag.getEmbeddedFlagExpression().charAt(2)); + return humanReadableFlags(sortedFlags); + } + + private static String humanReadableFlags(TreeSet flags) { + if (flags.isEmpty()) return ""; + StringBuilder f = new StringBuilder(flags.size() + 3); + f.append("(?"); + for (char flag : flags) f.append(flag); + return f.append(")").toString(); + } + + @Override + public void describeTo(final Description description) { + if (majorFault != null) { + description.appendText(majorFault); + return; + } + + description.appendText(matchOperation == MatchOperation.FIND ? "like \"" : "match \"") + .appendText(humanReadableFlags(regexProperties.getFlags())) + .appendText(pattern.pattern()) + .appendText("\""); + + if (successfulResultMatcher == null) { + if (matchOperation == MatchOperation.MATCH) { + final boolean isStartEvaluated + = start != null && start.isEvaluated(); + if (isStartEvaluated) { + /*description.appendText(" ") + .appendText(start.toString());*/ + if (start.hasFailure()) + description.appendText(" starts at ") + .appendText(start.getFailureDescription()); + } + + if (end != null && end.isEvaluated()) { + if (isStartEvaluated && start.hasFailure() && + start.group != end.group) + description.appendText(" in group ") + .appendText(Integer.toString(start.group)); + //description.appendText(end.toString()); + if (end.hasFailure()) { + if (isStartEvaluated && start.hasFailure()) + description.appendText(" and "); + description.appendText(" ends at ") + .appendText(end.getFailureDescription()); + description.appendText(" in group ") + .appendText(Integer.toString(end.group)); + } + } else if (isStartEvaluated) description.appendText("in group ") + .appendText(Integer.toString(start.group)); + } else { + final boolean isStartFailed = start != null + && start.expectedIndex.isExpectedIndexEvaluated() + && !start.expectedIndex.isExpectedIndexMatched(); + if (isStartFailed) { + description.appendText(" starts at "); + start.expectedIndex.describeTo(description); + } + + if (end != null + && end.expectedIndex.isExpectedIndexEvaluated() + && !end.expectedIndex.isExpectedIndexMatched()) { + if (isStartFailed) + description.appendText(" and"); + description.appendText(" ends at "); + end.expectedIndex.describeTo(description); + } + } + } else { + description.appendText(" on ") + .appendDescriptionOf(successfulResultMatcher); + } + } + + /** + * {@inheritDoc} + */ + @Override + public IEndMatcher startsAt(int group, Matcher index) { + if (successfulResultMatcher != null || start != null) + fail("not applicable when result matcher already specified"); + if (group < 0) + fail("negative group: " + group); + start = new StartPosition(group, index); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public IEndMatcher startsAt(Matcher index) { + return startsAt(0, index); + } + + /** + * {@inheritDoc} + */ + @Override + public IEndMatcher startsAt(int group, int index) { + if (successfulResultMatcher != null || start != null) + fail("not applicable when result matcher already specified"); + if (group < 0) + fail("negative group: " + group); + start = new StartPosition(group, equalTo(index)); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public IEndMatcher startsAt(int index) { + return startsAt(0, index); + } + + /** + * {@inheritDoc} + */ + @Override + public IStartMatcher endsAt(int group, Matcher index) { + if (successfulResultMatcher != null || end != null) + fail("not applicable when result matcher already specified"); + if (group < 0) + fail("negative group: " + group); + end = new EndPosition(group, index); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public IStartMatcher endsAt(Matcher index) { + return endsAt(0, index); + } + + /** + * {@inheritDoc} + */ + @Override + public IStartMatcher endsAt(int group, int index) { + if (successfulResultMatcher != null || end != null) + fail("not applicable when result matcher already specified"); + if (group < 0) + fail("negative group: " + group); + end = new EndPosition(group, equalTo(index)); + return this; + } + + /** + * {@inheritDoc} + */ + @Override + public IStartMatcher endsAt(int index) { + return endsAt(0, index); + } + + /** + * Attempts to match the entire region of the input sequence against the given pattern. + *

+ * The pattern is specified by the first method parameter. + *

+ * If the match succeeds, then more information can be obtained via the + * startsAt, endsAt. + *

+ * @param pattern a pattern to match + * @param an actual input sequence + * @return this matcher + * @throws AssertionError (a unit failure) + * if pattern is null; or if the expression's syntax is invalid in the pattern. + */ + @org.hamcrest.Factory + public static IsRegex match(String pattern) { + return new IsRegex(pattern, null, MatchOperation.MATCH, new RegexProperties()); + } + + /** + * Attempts to match the entire region of the input sequence against the given pattern + * and matches successful results with given expectations in successfulResultMatcher. + *

+ * The pattern is specified by the first method parameter, and the matched result is expected + * to match with the another matcher Matcher> specified by the + * last method parameter. + *

+ * @param pattern + * a pattern to match + * @param successfulResultMatcher + * Hamcrest matcher applied after successful match of input sequence with the given pattern + * @param an actual input sequence + * @param a generic parameter on successfulResultMatcher + * @return a Hamcrest's matcher reference + * @throws AssertionError (a unit failure) + * if pattern or successfulResultMatcher is null; or if the expression's syntax is invalid in the pattern. + */ + @org.hamcrest.Factory + public static > Matcher match(String pattern, Matcher successfulResultMatcher) { + assertNotNull("successfulResultMatcher is null", successfulResultMatcher); + return new IsRegex(pattern, successfulResultMatcher, MatchOperation.MATCH, new RegexProperties()); + } + + /** + * Attempts to match the given region of the input sequence against the given pattern. + *

+ * The pattern is specified by the first method parameter, and the matcher properties are given + * in the last parameter regexProperties. + *

+ * @param pattern a pattern to match + * @param regexProperties + * input sequence properties, and properties of this matcher of regular expression + * @param an actual input sequence + * @return this matcher + * @throws AssertionError (a unit failure) + * if pattern or regexProperties is null; or if the expression's syntax is invalid in the pattern. + */ + @org.hamcrest.Factory + public static IsRegex match(String pattern, RegexProperties regexProperties) { + return new IsRegex(pattern, null, MatchOperation.MATCH, regexProperties); + } + + /** + * Attempts to match the given region of the input sequence against the given pattern + * and matches successful results with given expectations in successfulResultMatcher. + *

+ * The pattern is specified by the first method parameter. The properties are given in the + * second parameter, and the matched result is expected to match with the another matcher + * Matcher> specified by the last method parameter. + *

+ * @param pattern a pattern to match + * @param regexProperties + * input sequence properties, and properties of this matcher of regular expression + * @param successfulResultMatcher + * Hamcrest matcher applied after successful match of input sequence with the given pattern + * @param an actual input sequence + * @param a generic parameter on successfulResultMatcher + * @return a Hamcrest's matcher reference + * @throws AssertionError (a unit failure) + * if pattern or regexProperties or successfulResultMatcher is null + * ; or if the expression's syntax is invalid in the pattern. + */ + @org.hamcrest.Factory + public static > Matcher match(String pattern, RegexProperties regexProperties, Matcher successfulResultMatcher) { + assertNotNull("successfulResultMatcher is null", successfulResultMatcher); + return new IsRegex(pattern, successfulResultMatcher, MatchOperation.MATCH, regexProperties); + } + + /** + * Attempts to find all input subsequences against the given pattern. + *

+ * The pattern is specified by the first method parameter. + *

+ * If the match succeeds, then more information can be obtained via the + * startsAt, endsAt. + *

+ * @param pattern a pattern to match + * @param an actual input sequence + * @return this matcher + * @throws AssertionError (a unit failure) + * If pattern is null; or if the expression's syntax is invalid in the pattern. + */ + @org.hamcrest.Factory + public static IsRegex like(String pattern) { + return new IsRegex(pattern, null, MatchOperation.FIND, new RegexProperties()); + } + + /** + * Attempts to find all input subsequences against the given pattern which match + * their successful results with given expectations in successfulResultMatcher. + *

+ * The pattern is specified by the first method parameter, and the matched result is expected + * to match with the another matcher Matcher> specified by the + * last method parameter. + *

+ * @param pattern + * a pattern to match + * @param successfulResultMatcher + * Hamcrest matcher applied after successful match of input sequence with the given pattern + * @param an actual input sequence + * @param a generic parameter on successfulResultMatcher + * @return a Hamcrest's matcher reference + * @throws AssertionError (a unit failure) + * if pattern or successfulResultMatcher is null; or if the expression's syntax is invalid in the pattern. + */ + @org.hamcrest.Factory + public static > Matcher like(String pattern, Matcher successfulResultMatcher) { + assertNotNull("successfulResultMatcher is null", successfulResultMatcher); + return new IsRegex(pattern, successfulResultMatcher, MatchOperation.FIND, new RegexProperties()); + } + + /** + * Attempts to find all input subsequences against the given pattern. + *

+ * The pattern is specified by the first method parameter, and the matcher properties are given + * in the last parameter regexProperties. + *

+ * @param pattern a pattern to match + * @param regexProperties + * input sequence properties, and properties of this matcher of regular expression + * @param an actual input sequence + * @return this matcher + * @throws AssertionError (a unit failure) + * if pattern or regexProperties is null; or if the expression's syntax is invalid in the pattern. + */ + @org.hamcrest.Factory + public static IsRegex like(String pattern, RegexProperties regexProperties) { + return new IsRegex(pattern, null, MatchOperation.FIND, regexProperties); + } + + /** + * Attempts to find all input subsequences against the given pattern which match + * their successful results with given expectations in successfulResultMatcher. + *

+ * The pattern is specified by the first method parameter. The properties are given in the + * second parameter, and the matched result is expected to match with the another matcher + * Matcher> specified by the last method parameter. + *

+ * @param pattern a pattern to match + * @param regexProperties + * input sequence properties, and properties of this matcher of regular expression + * @param successfulResultMatcher + * Hamcrest matcher applied after successful match of input sequence with the given pattern + * @param an actual input sequence + * @param a generic parameter on successfulResultMatcher + * @return a Hamcrest's matcher reference + * @throws AssertionError (a unit failure) + * if pattern or regexProperties or successfulResultMatcher is null + * ; or if the expression's syntax is invalid in the pattern. + */ + @org.hamcrest.Factory + public static > Matcher like(String pattern, RegexProperties regexProperties, Matcher successfulResultMatcher) { + assertNotNull("successfulResultMatcher is null", successfulResultMatcher); + return new IsRegex(pattern, successfulResultMatcher, MatchOperation.FIND, regexProperties); + } +} diff --git a/matchers/src/main/java/org/junit/contrib/matchers/IsRegexResult.java b/matchers/src/main/java/org/junit/contrib/matchers/IsRegexResult.java new file mode 100644 index 0000000..6193ab9 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/IsRegexResult.java @@ -0,0 +1,216 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.internal.matchers.TypeSafeMatcher; + +import java.util.Arrays; + +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.*; +import static org.junit.contrib.matchers.ResultType.*; + +/** + * The purpose of this matcher is an use in {@link IsRegex regex matcher}. + * Note: Any likely combination of start and group within combinatory + * matchers (e.g. Hamcrest's allOf) is not same as the matcher startByGroup. + *

+ * Same statement implies to end and group towards endByGroup. + *

+ * Note: Any likely combination of start and end within combinatory + * matchers (e.g. Hamcrest's allOf) is not same as the matcher region or regionByGroup. + *

+ * @author tibor17 + * @version 0.1 + * @see org.junit.contrib.matchers.IsRegex + * @since 0.1, 25.12.2011, 22:20 + */ +public final class IsRegexResult> extends TypeSafeMatcher { + static final int ANY_GROUP = -1; + + private final int group; + private final ResultType resultType; + private final Matcher[] expectedResult; + + private String generalFault; + + private IsRegexResult(int group, ResultType resultType, Matcher... expectedResult) { + this.group = group; + this.resultType = resultType; + this.expectedResult = expectedResult; + } + + private IsRegexResult(ResultType resultType, Matcher... expectedResult) { + this(ANY_GROUP, resultType, expectedResult); + } + + @Override + public boolean matchesSafely(T t) { + switch(resultType) { + case GROUP: + for (final RegexResult regexResult : t) + if (expectedResult[0].matches(regexResult.getGroup())) + return true; + break; + case START: + for (final RegexResult regexResult : t) + if ((group == ANY_GROUP || group == regexResult.getGroup()) + && expectedResult[0].matches(regexResult.getStart())) + return true; + break; + case END: + for (final RegexResult regexResult : t) + if ((group == ANY_GROUP || group == regexResult.getGroup()) + && expectedResult[0].matches(regexResult.getEnd())) + return true; + break; + case REGION: + for (final RegexResult regexResult : t) + if ((group == ANY_GROUP || group == regexResult.getGroup()) + && expectedResult[0].matches(regexResult.getStart()) + && expectedResult[1].matches(regexResult.getEnd())) + return true; + break; + default: + generalFault = "expected " + resultType + " of " + Arrays.toString(ResultType.values()); + } + return false; + } + + @Override + public void describeTo(Description description) { + if (generalFault == null) { + switch (resultType) { + case GROUP: + description.appendText(resultType.name().toLowerCase()) + .appendText(" ") + .appendDescriptionOf(expectedResult[0]); + break; + case START: + description.appendText(resultType.name().toLowerCase()) + .appendText(" position ") + .appendDescriptionOf(expectedResult[0]); + if (group != ANY_GROUP) + description.appendText(" group ") + .appendText(Integer.toString(group)); + break; + case END: + description.appendText(resultType.name().toLowerCase()) + .appendText(" position ") + .appendDescriptionOf(expectedResult[0]); + if (group != ANY_GROUP) + description.appendText(" group ") + .appendText(Integer.toString(group)); + break; + case REGION: + description.appendText(resultType.name().toLowerCase()) + .appendText(" from ") + .appendDescriptionOf(expectedResult[0]); + description.appendText(" to ") + .appendDescriptionOf(expectedResult[1]); + if (group != ANY_GROUP) + description.appendText(" group ") + .appendText(Integer.toString(group)); + break; + default: + fail(" an internal error"); + } + } else fail(generalFault); + } + + @org.hamcrest.Factory + public static > Matcher group(Matcher group) { + assertNotNull(group); + return new IsRegexResult(GROUP, group); + } + + @org.hamcrest.Factory + public static > Matcher group(int group) { + return group(equalTo(group)); + } + + @org.hamcrest.Factory + public static > Matcher start(Matcher start) { + assertNotNull(start); + return new IsRegexResult(START, start); + } + + @org.hamcrest.Factory + public static > Matcher start(int start) { + return start(equalTo(start)); + } + + @org.hamcrest.Factory + public static > Matcher startByGroup(int group, Matcher start) { + assertTrue("group " + group + " must be non-negative", group >= 0); + assertNotNull(start); + return new IsRegexResult(group, START, start); + } + + @org.hamcrest.Factory + public static > Matcher startByGroup(int group, int start) { + return startByGroup(group, equalTo(start)); + } + + @org.hamcrest.Factory + public static > Matcher end(Matcher end) { + assertNotNull(end); + return new IsRegexResult(END, end); + } + + @org.hamcrest.Factory + public static > Matcher end(int end) { + return end(equalTo(end)); + } + + @org.hamcrest.Factory + public static > Matcher endByGroup(int group, Matcher end) { + assertTrue("group " + group + " must be non-negative", group >= 0); + assertNotNull(end); + return new IsRegexResult(group, END, end); + } + + @org.hamcrest.Factory + public static > Matcher endByGroup(int group, int end) { + return endByGroup(group, equalTo(end)); + } + + @org.hamcrest.Factory + public static > Matcher region(Matcher start, Matcher end) { + assertNotNull(start); + assertNotNull(end); + return new IsRegexResult(REGION, start, end); + } + + @org.hamcrest.Factory + public static > Matcher region(int start, int end) { + return region(equalTo(start), equalTo(end)); + } + + @org.hamcrest.Factory + public static > Matcher regionByGroup(int group, Matcher start, Matcher end) { + assertTrue("group " + group + " must be non-negative", group >= 0); + assertNotNull(start); + assertNotNull(end); + return new IsRegexResult(group, REGION, start, end); + } + + @org.hamcrest.Factory + public static > Matcher regionByGroup(int group, int start, int end) { + return regionByGroup(group, equalTo(start), equalTo(end)); + } +} diff --git a/matchers/src/main/java/org/junit/contrib/matchers/IsThrowable.java b/matchers/src/main/java/org/junit/contrib/matchers/IsThrowable.java new file mode 100644 index 0000000..ff2a3ae --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/IsThrowable.java @@ -0,0 +1,209 @@ +package org.junit.contrib.matchers; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.internal.matchers.TypeSafeMatcher; + +import static org.junit.Assert.assertNotNull; + +/** + * This is an utility of matchers mainly used together with the {@linkplain IsThrowing IsThrowing matcher}. + * The matchers are used to match the type of an exception, cause, message, and localized message. Thus the + * the returned reference is a matcher which corresponds to a reference of an actual exception. So the following + * methods are called for certain matchers: + *

+ *

    + *
  • {@linkplain Object#getClass() getClass()} for {@linkplain #type(org.hamcrest.Matcher) the type matcher} + *
  • {@linkplain Throwable#getCause() getCause()} for {@linkplain #cause(org.hamcrest.Matcher) the cause matcher} + *
  • {@linkplain Throwable#getMessage() getMessage()} for {@linkplain #message(org.hamcrest.Matcher) the message matcher} + *
  • {@linkplain Throwable#getLocalizedMessage() getLocalizedMessage()} for {@linkplain #localizedMessage(org.hamcrest.Matcher)} the localized message matcher + *
+ *

+ *

+ * As an use case, this assert will succeed because the type of actual instance of + * exception of {@link ArrayIndexOutOfBoundsException} is a subtype of the expected type + * {@link IndexOutOfBoundsException} declared by the combination of matchers + * type(assignableTo(IndexOutOfBoundsException.class)): + *

+ *

+ *
+ *          assertThat(new ArrayIndexOutOfBoundsException(),
+                    is(type(assignableTo(IndexOutOfBoundsException.class))));
+ *      
+ *
+ *

+ */ +public final class IsThrowable { + private IsThrowable() throws IllegalAccessException { + throw new IllegalAccessException("constructor not reachable"); + } + + @org.hamcrest.Factory + public static
Matcher localizedMessage(final Matcher expectedLocalizedMessage) { + assertNotNull("expectedLocalizedMessage is null", expectedLocalizedMessage); + return new TypeSafeMatcher() { + private boolean isMatchesCalled; + + @Override public boolean matchesSafely(A e) { + isMatchesCalled = true; + return expectedLocalizedMessage.matches(e.getLocalizedMessage()); + } + + @Override public void describeTo(Description description) { + if (isMatchesCalled) description.appendText("localized message ") + .appendDescriptionOf(expectedLocalizedMessage); + else description.appendText("<>"); + } + }; + } + + @org.hamcrest.Factory + public static Matcher localizedMessage(final IsRegex expectedLocalizedMessagePattern) { + assertNotNull("expectedLocalizedMessagePattern is null", expectedLocalizedMessagePattern); + return new TypeSafeMatcher() { + private boolean isMatchesCalled, isNullMessage; + + @Override public boolean matchesSafely(A a) { + isMatchesCalled = true; + final String msg = a.getMessage(); + isNullMessage = msg == null; + return !isNullMessage && expectedLocalizedMessagePattern.matches(msg); + } + + @Override public void describeTo(Description description) { + if (!isMatchesCalled) description.appendText("<>"); + else if (isNullMessage) description.appendText("localized message "); + else description.appendText("localized message ").appendDescriptionOf(expectedLocalizedMessagePattern); + } + }; + } + + @org.hamcrest.Factory + public static Matcher message(final Matcher expectedMessage) { + assertNotNull("expectedMessage is null", expectedMessage); + return new TypeSafeMatcher() { + private boolean isMatchesCalled; + + @Override public boolean matchesSafely(A a) { + isMatchesCalled = true; + return expectedMessage.matches(a.getMessage()); + } + + @Override public void describeTo(Description description) { + if (isMatchesCalled) description.appendText("message ").appendDescriptionOf(expectedMessage); + else description.appendText("<>"); + } + }; + } + + @org.hamcrest.Factory + public static Matcher message(final IsRegex expectedMessagePattern) { + assertNotNull("expectedMessagePattern is null", expectedMessagePattern); + return new TypeSafeMatcher() { + private boolean isMatchesCalled, isNullMessage; + + @Override public boolean matchesSafely(A a) { + isMatchesCalled = true; + final String msg = a.getMessage(); + isNullMessage = msg == null; + return !isNullMessage && expectedMessagePattern.matches(msg); + } + + @Override public void describeTo(Description description) { + if (!isMatchesCalled) description.appendText("<>"); + else if (isNullMessage) description.appendText("message "); + else description.appendText("message ").appendDescriptionOf(expectedMessagePattern); + } + }; + } + + @org.hamcrest.Factory + public static Matcher type(final Matcher> expectedType) { + assertNotNull("expectedType is null", expectedType); + return new TypeSafeMatcher() { + private boolean isMatchesCalled; + @Override public boolean matchesSafely(A a) { + isMatchesCalled = true; + return expectedType.matches(a.getClass()); + } + + @Override public void describeTo(Description description) { + if (isMatchesCalled) description.appendText("type ").appendDescriptionOf(expectedType); + else description.appendText("<>"); + } + }; + } + + @org.hamcrest.Factory + public static Matcher cause(final Matcher expectedCause) { + assertNotNull("expectedCause is null", expectedCause); + return new TypeSafeMatcher() { + private boolean isMatchesCalled; + @Override public boolean matchesSafely(A a) { + isMatchesCalled = true; + return expectedCause.matches(a.getCause()); + } + + @Override public void describeTo(Description description) { + if (isMatchesCalled) description.appendText("cause ").appendDescriptionOf(expectedCause); + else description.appendText("<>"); + } + }; + } + + @org.hamcrest.Factory + public static Matcher any() { + return new BaseMatcher() { + private boolean isMatchesCalled; + @Override + public boolean matches(Object thrown) { + isMatchesCalled = true; + return thrown instanceof Throwable; + } + + @Override + public void describeTo(Description description) { + description.appendText(isMatchesCalled ? "any Throwable" : "<>"); + } + }; + } + + @org.hamcrest.Factory + public static Matcher complementOf(final Matcher unexpectedCause) { + assertNotNull("unexpectedCause is null", unexpectedCause); + return new TypeSafeMatcher() { + private boolean isMatchesCalled; + @Override + public boolean matchesSafely(A a) { + isMatchesCalled = true; + return !unexpectedCause.matches(a); + } + + @Override + public void describeTo(Description description) { + if (isMatchesCalled) description.appendText("complement of ") + .appendDescriptionOf(unexpectedCause); + else description.appendText("<>"); + } + }; + } + + @org.hamcrest.Factory + public static Matcher none() { + return new BaseMatcher() { + private boolean isMatchesCalled; + @Override + public boolean matches(Object a) { + isMatchesCalled = true; + return !(a instanceof Throwable); + } + + @Override + public void describeTo(Description description) { + if (isMatchesCalled) description.appendText("none"); + else description.appendText("<>"); + } + }; + } +} diff --git a/matchers/src/main/java/org/junit/contrib/matchers/IsThrowing.java b/matchers/src/main/java/org/junit/contrib/matchers/IsThrowing.java new file mode 100644 index 0000000..6e77db6 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/IsThrowing.java @@ -0,0 +1,591 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; +import org.junit.internal.matchers.TypeSafeMatcher; + +import java.util.ArrayList; + +import static org.hamcrest.core.AnyOf.anyOf; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.fail; +import static org.junit.Assert.assertTrue; +import static org.junit.contrib.matchers.IsAssignableTo.assignableToThrowable; + +/** + * These are matchers used together with the {@link org.junit.Assert#assertThat(Object, org.hamcrest.Matcher) assertThat} + * method, however they are not currently included in JUnitMatchers. + *

The purpose of them is to encapsulate a code into a block, see wrapping blocks {@link Block} and {@link Callable}, and + * impose exceptional conditions on how the block returns (normally or abruptly).

This is especially useful in + * situations, when a surrounding code in test case should not throw at all, or throws other expected exceptions. + *

The names of matchers which are finished by 'Deeply' report Java stack trace, cause and localized message, when + * the running block of code has returned unexpectedly. + *

This matcher comes from practical experiences when trying to solve the following test scenario. The test case is + * testing code statements, where their calls would not split to other methods and call a sequence the statements + * matters a lot. Thus suppose this formal test case:

+ *

+ *
+ *      @Test(expected = SomeException.class)
+ *      public void myTest() {
+ *          (statement)
+ *          (statement)
+ *
+ *          This block should not throw any exception
+ *          {
+ *              (statement)
+ *              (statement)
+ *              (statement)
+ *          }
+ *
+ *          (statement)
+ *          (statement)
+ *      }
+ *  
+ *
+ *

In this particular scenario the developer dos not want to encapsulate the middle part into an ugly and dangerous + * try-catch block. Here the test behaviour wants to say that all statements are supposed to throw exceptions, except for + * the middle part. This means that we want to keep Hamcrest in use with assertThat() and keep the tests as much verbose + * as possible. When you read the expression using assertThat(), you can understand what it does. + *

The matchers in IsThrowing have eight variants, where the second half deals with stack trace from the + * failing block to a description (output stream):

+ *

+ *
+ *      throwing(Class<? extends Throwable>...  ): IThrownMessage<Void, ? extends Block>
+ *      throwing(Matcher<Class<? extends Throwable>): IThrownMessage<V, ? extends Callable>
+ *      throwing(Matcher<? extends Throwable>): Matcher<? extends Callable>
+ *      notThrowing(Class<? extends Throwable>...  ): IThrownMessage<V, ? extends Callable>
+ *      throwingDeeply(Class<? extends Throwable>...  ): IThrownMessage<Void, ? extends Block>
+ *      throwingDeeply(Matcher<Class<? extends Throwable>): IThrownMessage<V, ? extends Callable>
+ *      throwingDeeply(Matcher<? extends Throwable>): Matcher<? extends Callable>
+ *      notThrowingDeeply(Class<? extends Throwable>...  ): IThrownMessage<V, ? extends Callable>
+ *  
+ *
+ *

The form of is(not(throwing())) is equivalent to the form is(notThrowing()). Similar with + * the form of is(not(throwingDeeply())) which is again equivalent to is(notThrowingDeeply()). + * Once you use the 'Deeply', the stack trace of failed block is observed. The allOf and + * anyOf, and/or combination forms of multiple matchers would not be much readable, however still correct. + *

Finally the expression may continue with {@link #andMessage(org.hamcrest.Matcher)} or {@link #andMessage(IsRegex)} + * which additionally filters the expected exceptions against certainly localized message + * (via Matcher<String> or the regular expression) of thrown exception in the block. + * Thus, the entire expression appears in a form of assertThat(block, is(throwing().andMessage())).

+ *

The following table describes a behavior and possible examples of the IsThrowing matcher. + *

+ * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Use Case of IsThrowingExplanation
assertThat(block, is(throwing()))the 'block' is expected to throw any exception
assertThat(block, is(throwingDeeply()))the 'block' is expected to throw any exception with block's stack trace (not an error in 'block')
assertThat(block, is(notThrowing())) or assertThat(block, is(not(throwing())))the 'block' is expected not to throw exceptions at all
assertThat(block, is(notThrowingDeeply())) or assertThat(block, is(not(throwingDeeply())))the 'block' is expected not to throw exceptions at all (including block's stack trace if failure in it)
assertThat(block, is(throwing(ArrayIndexOutOfBoundsException.class, IndexOutOfBoundsException.class)))the 'block' is expected to throw all assignable exceptions to java.lang.ArrayIndexOutOfBoundsException and java.lang.IndexOutOfBoundsException
assertThat(block, is(throwingDeeply(ArrayIndexOutOfBoundsException.class, IndexOutOfBoundsException.class)))the 'block' is expected to throw all assignable exceptions to java.lang.ArrayIndexOutOfBoundsException and java.lang.IndexOutOfBoundsException (including block's stack trace if other failure in it). If other exception is thrown, the deep stack trace appears an error in the 'block'.
assertThat(block, is(notThrowing(ArrayIndexOutOfBoundsException.class, IndexOutOfBoundsException.class))) or assertThat(block, is(not(throwing(ArrayIndexOutOfBoundsException.class, IndexOutOfBoundsException.class))))the 'block' is expected to throw all exceptions except for those which are assignable to java.lang.ArrayIndexOutOfBoundsException or java.lang.IndexOutOfBoundsException
assertThat(block, is(notThrowingDeeply(ArrayIndexOutOfBoundsException.class, IndexOutOfBoundsException.class))) or assertThat(block, is(not(throwingDeeply(ArrayIndexOutOfBoundsException.class, IndexOutOfBoundsException.class))))the 'block' is expected to throw all exceptions except for those which are assignable to java.lang.ArrayIndexOutOfBoundsException or java.lang.IndexOutOfBoundsException; Otherwise a stack trace appears from the 'block'
+ *

+ * Note: Assignable exceptions means all specified and their sub-types.

+ *

Concrete use cases:

+ * A lot of use cases are covered in the tests, see TestIsThrowing. + *

This is the most simple use case: + *

+ *      final Integer[] array = new Integer[2];
+ *      IBlock block = new IBlock() {
+ *          public void run() {
+ *              int i = 0;
+ *              array[i++] = i;
+ *              array[i++] = i;
+ *              array[i++] = i;
+ *          }
+ *      };
+ *      assertThat(block, is(throwing(ArrayIndexOutOfBoundsException.class)));
+ *      array[1] = array[0];
+ * 

+ *

A description can be placed on the exceptional conditions using {@link org.hamcrest.core.DescribedAs}, in a form + * as follows:

+ *      expected = {IndexOutOfBoundsException.class, ArrayStoreException.class};
+ *      assertThat(block, describedAs("\n%0 <why should 0>,\n%1 <why should 1>", is(throwingDeeply(expected)), expected))
+ *  

+ * This matcher can be also used in multiple times running block within one call of + * {@link org.junit.Assert#assertThat(Object, org.hamcrest.Matcher)} if these matchers are encapsulated by + * {@link org.junit.matchers.JUnitMatchers#both(org.hamcrest.Matcher)} and and(), or() matchers. + *

+ *      List list = Collections.checkedList(Arrays.asList("b", "c"), String.class);
+ *      final ListIterator it = list.listIterator();
+ *      IBlock block = new IBlock() {
+ *          public void run() {
+ *              it.next();
+ *              it.next();
+ *              it.set("a");
+ *          }
+ *      };
+ *      assertThat(block, both(is(throwingDeeply(NoSuchElementException.class))).and(is(notThrowing())));
+ *  

+ *

Other alternative of both() matcher would be Hamcrest's allOf() matcher: + *

+ *      List list = Collections.checkedList(Arrays.asList("b", "c"), String.class);
+ *      final ListIterator it = list.listIterator();
+ *      IBlock block = new IBlock() {
+ *          public void run() {
+ *              it.next();
+ *              it.next();
+ *              it.set("a");
+ *          }
+ *      };
+ *      assertThat(block, allOf(is(notThrowing()), is(throwingDeeply(NoSuchElementException.class))));
+ *  
+ *

+ * The order in which the block is launched by every matcher by allOf() is not same as if used in + * both() matcher. The both().or() and anyOf() can be used as an alternative in + * other cases as well, when the block is expected to be reused in a test case in an order within one call of + * assertThat(). + *

+ *

+ * Other two examples would describe situations when you want to assert against exceptions and expected messages. The + * first example shows the use of regular expression: + *

+ *      List list = Collections.checkedList(Arrays.asList("b", "c"), String.class);
+ *      final ListIterator it = list.listIterator();
+ *      IBlock block = new IBlock() {
+ *          public void run() {
+ *              throw new Exception("     a ny thin g     ");
+ *          }
+ *      };
+ *      assertThat(block, is(throwingDeeply().andMessage("a ny|thing")));
+ *  
+ *

The second example uses Matcher combinations on a thrown message: + *

+ *      List list = Collections.checkedList(Arrays.asList("b", "c"), String.class);
+ *      final ListIterator it = list.listIterator();
+ *      IBlock block = new IBlock() {
+ *          public void run() {
+ *              throw new Exception("     a ny thin g     ");
+ *          }
+ *      };
+ *      assertThat(block, is(throwingDeeply().andMessage(anyOf(containsString("any"), containsString("thin"), containsString("thing")))));
+ *  

+ *

+ *

Known limitations: + * The assertThat() fails as soon as identifying that no exceptions are required to throw + * (i.e. using {@link #notThrowing(Class[])}, {@link #notThrowingDeeply(Class[])} with empty parameter list) + * and an expected thrown message is specified (even if null) via {@link #andMessage(org.hamcrest.Matcher)}, and + * {@link #andMessage(IsRegex)}. + *

+ * @author Tibor17 + * @version 0.1 + * @since 0.1, Sep 15, 2011, 10:33:33 AM + */ +public class IsThrowing> + extends TypeSafeMatcher implements IIsThrowingMessage, IThrownCallable { + @SuppressWarnings("unchecked") + private static final Class[] ALL_THROWABLE = new Class[]{Throwable.class}; + + public static enum Operation { + THROWING("throwing"), + NOT_THROWING("notThrowing"), + THROWING_DEEPLY("throwingDeeply"), + NOT_THROWING_DEEPLY("notThrowingDeeply"); + + private final String operation; + private Operation(String operation) { + this.operation = operation; + } + + public final @Override String toString() { + return operation; + } + } + + public static enum MatcherType { + THROWABLE_CLASSES, THROWABLE_INSTANCES + } + + private final Matcher> throwableClassesMatcher; + private final Matcher throwableInstancesMatcher; + private final boolean isThrowing, showStack, isThrownMessageMatcherEnabled; + private final StringBuilder additionalErrMsg = new StringBuilder(); + private final MatcherType matcherType; + + private Throwable caughtException; + private ThrownMessageMatcher thrownMessageMatcher; + private V returnedValue, fallback; + private boolean isBlockReturnedNormally, isMatchesCalled; + + private IsThrowing() { + throwableClassesMatcher = anyOf(); + isThrowing = showStack = isThrownMessageMatcherEnabled = false; + matcherType = null; + throwableInstancesMatcher = null; + } + + private IsThrowing(Class expectedType) { + super(expectedType); + throwableClassesMatcher = anyOf(); + isThrowing = showStack = isThrownMessageMatcherEnabled = false; + matcherType = null; + throwableInstancesMatcher = null; + } + + /** + * Specifies expected behaviour when running the block of code. + *

+ * @param operation an operation, representing operation names e.g., throwing, notThrowing, throwingDeeply, and notThrowingDeeply + * @param isThrowing {@code true} if any of the given exceptions is expected to be thrown by calling the + * {@link Block#run()}. + *

If no exceptions are specified and this parameter is {@code true}, then any exception is expected to be + * thrown when running the block of code. + *

If {@code false}, then a complementary exception is expected to throw when running the block. + *

If {@code false} and no exceptions are specified at all, then no exception is expected to be thrown + * (complement of all is nothing). + * @param showStack If {@code true} and an unexpected exception (other than exceptions) is thrown in + * running block, then Java Stack Trace is passed in the description on method's parameter of + * {@link #describeTo(org.hamcrest.Description)}. + * @param throwableClassesMatcher Expected exceptions which belong to the particular operation. An empty array of exceptions, or + * the {@link Throwable Throwable.class} in array represents all exceptions. + */ + public IsThrowing(final Operation operation, final boolean isThrowing, final boolean showStack, + final Class... throwableClassesMatcher) { + assertNotNull("an array of exceptions must not be null", throwableClassesMatcher); + this.throwableClassesMatcher = anyOfThrowable(operation, throwableClassesMatcher.length == 0 ? ALL_THROWABLE : throwableClassesMatcher); + throwableInstancesMatcher = null; + this.showStack = showStack; + this.isThrowing = isThrowing; + isThrownMessageMatcherEnabled = true; + if (throwableClassesMatcher.length != 0) additionalErrMsg.append(toString(throwableClassesMatcher)); + matcherType = IsThrowing.MatcherType.THROWABLE_CLASSES; + } + + /** + * Specifies expected behaviour when running the block of code. + *

+ * @param isThrowing {@code true} if used by static matcher. + * @param showStack If {@code true} and an unexpected exception is thrown in running block, then appeared Java Stack + * Trace appears in the description on method's parameter of {@link #describeTo(org.hamcrest.Description)}. + * @param throwableClassesMatcher A Matcher of expected exceptions which belong to 'throwing' operation. The actual exception + * thrown by the {@link B block} is caught internally and never null. + */ + public IsThrowing(final boolean isThrowing, final boolean showStack, final Matcher> throwableClassesMatcher) { + assertNotNull("exceptions-matcher must not be null", throwableClassesMatcher); + this.throwableClassesMatcher = throwableClassesMatcher; + throwableInstancesMatcher = null; + this.showStack = showStack; + this.isThrowing = isThrowing; + isThrownMessageMatcherEnabled = true; + matcherType = IsThrowing.MatcherType.THROWABLE_CLASSES; + } + + /** + * Specifies expected behaviour when running the block of code. + *

+ * @param showStack If {@code true} and an unexpected exception is thrown in running block, then appeared Java Stack + * Trace appears in the description on method's parameter of {@link #describeTo(org.hamcrest.Description)}. + * @param throwableInstancesMatcher A Matcher of expected exceptions which belong to 'throwing' operation. The actual exception + * thrown by the {@link B block} is caught internally. Uncaught exception instance, as a null, should be still possible to + * evaluate in fail-safe matcher of the given one throwableInstancesMatcher. + */ + public IsThrowing(final boolean showStack, final Matcher throwableInstancesMatcher) { + assertNotNull("exceptions-matcher must not be null", throwableInstancesMatcher); + throwableClassesMatcher = null; + this.throwableInstancesMatcher = throwableInstancesMatcher; + this.showStack = showStack; + this.isThrowing = true; + isThrownMessageMatcherEnabled = false; + matcherType = IsThrowing.MatcherType.THROWABLE_INSTANCES; + } + + private static Matcher> anyOfThrowable(final Operation operation, final Class... exceptions) { + assertNotNull("an array of exceptions must not be null", exceptions); + return anyOf(new ArrayList>>(){{ + for (final Class t : exceptions) { + assertNotNull("an array of exceptions in the operation " + + getClass().getName() + "." + operation.toString() + "()" + + " must not have null elements", t); + add(assignableToThrowable(t)); + }}}); + } + + private static String toString(final Class... exceptions) { + int i = exceptions.length; + final StringBuilder description = new StringBuilder("["); + for (final Class t : exceptions) { + description.append(t.getName()); + if (--i != 0) description.append(", "); + } + return description.append("]").toString(); + } + + /** + * Evaluates whether the block returned executed without throwing any exception. + * @return {@code true} if block executed without throwing any exception + */ + @Override + public final boolean isBlockReturnedNormally() { + return isBlockReturnedNormally; + } + + /** + * See {@link Callable}. + * @return a value returned by a callable block, or a fallback when callable block threw + */ + @Override + public final V blockReturned() { + return returnedValue; + } + + /** + * See the parameter in {@linkplain Callable constructor of Callable}. + * @return a fallback of a callable block which returns + */ + @Override + public final V fallback() { + return fallback; + } + + /** + * The method parameter 'block' has expected type {@link B}, and its value is not null. + * @param block a block of testing code + * @return {@code true} if matching expectations + */ + @Override + public boolean matchesSafely(B block) { + isMatchesCalled = true; + + fallback = block.fallback; + returnedValue = block.evaluate(); + isBlockReturnedNormally = block.isSucceeded; + if (!isBlockReturnedNormally) + caughtException = block.throwable; + + boolean match; + + switch (matcherType) { + case THROWABLE_CLASSES: + match = !isThrowing; + if (!isBlockReturnedNormally) + match ^= throwableClassesMatcher.matches(caughtException.getClass()); + + if (thrownMessageMatcher != null && caughtException != null) { + if (match) match = thrownMessageMatcher.isMatching(caughtException); + additionalErrMsg.append(additionalErrMsg.length() == 0 ? "" : " ") + .append(thrownMessageMatcher.createErrorMessage(caughtException)); + } + break; + case THROWABLE_INSTANCES: + match = throwableInstancesMatcher.matches(caughtException); + break; + default: + fail("unknown throwable type " + matcherType); + throw new IllegalStateException("shouldn't appear in an unreachable statement"); + } + + return match; + } + + @Override + public void describeTo(Description description) { + if (!isMatchesCalled) { + description.appendText("<>"); + return; + } + + final boolean showUnexpectedStackTrace = showStack & caughtException != null; + switch (matcherType) { + case THROWABLE_CLASSES: + if (matchesAllThrowableClasses()) { + if (isThrowing) description.appendText("to throw any exception"); + else description.appendText("not to throw exceptions at all"); + } else { + if (isThrowing) description.appendText("to throw exception(s)"); + else description.appendText("not to throw exception(s)"); + } + + if (additionalErrMsg.length() != 0) + description.appendText(" ").appendText(additionalErrMsg.toString()); + + if (showUnexpectedStackTrace) + describeThrown(description.appendText("\r\n"), caughtException); + break; + case THROWABLE_INSTANCES: + throwableInstancesMatcher.describeTo(description.appendText("throwing ")); + if (showUnexpectedStackTrace) + describeThrown(description.appendText("\r\n"), caughtException); + break; + default: + fail("unknown throwable type " + matcherType); + throw new IllegalStateException("shouldn't appear in an unreachable statement"); + } + } + + private static Description describeThrown(final Description description, final Throwable thrown) { + description.appendText(thrown.toString()).appendText("\r\n"); + for (final StackTraceElement s : thrown.getStackTrace()) + description.appendText(s.toString()).appendText("\r\n"); + return description; + } + + private boolean matchesAllThrowableClasses() { + try { + return throwableClassesMatcher.matches(Throwable.class); + } catch (VirtualMachineError e) { + throw e; + } catch (Throwable t) { + fail(t.getLocalizedMessage()); + throw new IllegalStateException("unreachable statement"); + } + } + + @Override + public final Matcher withMessage(final String expectedMessage) { + assertTrue("\"withMessage\" enabled only by matcher *throwing*(Class...) " + + "and *throwing*(Matcher>)", isThrownMessageMatcherEnabled); + return andMessage(new BaseMatcher() { + @Override + public boolean matches(Object actualMessage) { + return expectedMessage == null && actualMessage == null + || expectedMessage != null && expectedMessage.equals(actualMessage); + } + + @Override + public void describeTo(Description description) { + description.appendText(expectedMessage == null ? + "null message" : ("a message equal to " + expectedMessage)); + } + }); + } + + @Override + public final Matcher andMessage(final Matcher matchWith) { + assertNotNull("string-matcher must not be null", matchWith); + assertTrue("\"andMessage\" enabled only by matcher *throwing*(Class...) " + + "and *throwing*(Matcher>)", isThrownMessageMatcherEnabled); + thrownMessageMatcher = new ThrownMessageMatcher("and message", "and message", "", " ", "", "is ") { + @Override protected boolean isMatching(Throwable thrown) { return isMatchingMessageByHamcrest(thrown, matchWith); } + @Override protected boolean hasNullMessage(Throwable thrown) { return IsThrowing.hasNullMessage(thrown); } + @Override protected String getActualMessage(Throwable thrown) { return ""/*getMessage(thrown)*/; } + @Override protected Matcher getExpectedMatcher() { return matchWith; } + @Override protected String getExpectedMessage() { return null; } + }; + return this; + } + + @Override + public final Matcher andMessage(final IsRegex regex) { + assertNotNull("regular expression matcher must not be null", regex); + assertTrue("\"andMessage\" enabled only by matcher *throwing*(Class...) " + + "and *throwing*(Matcher>)", isThrownMessageMatcherEnabled); + thrownMessageMatcher = new ThrownMessageMatcher("and message", "and message", "", " ", "", "is ") { + @Override protected boolean isMatching(Throwable thrown) { return isMatchingMessageByRegex(thrown, regex); } + @Override protected boolean hasNullMessage(Throwable thrown) { return IsThrowing.hasNullMessage(thrown); } + @Override protected String getActualMessage(Throwable thrown) { return ""/*getMessage(thrown)*/; } + @Override protected Matcher getExpectedMatcher() { return regex; } + @Override protected String getExpectedMessage() { return null; } + }; + return this; + } + + private static String getMessage(final Throwable thrown) { + assertNotNull("(2) an instance of thrown exception must not be NULL", thrown); + return thrown.getLocalizedMessage(); + } + + private static boolean hasNullMessage(final Throwable thrown) { + assertNotNull("(0) an instance of thrown exception must not be NULL", thrown); + return thrown.getLocalizedMessage() == null; + } + + private static boolean isMatchingMessageByRegex(final Throwable thrown, final IsRegex regex) { + assertNotNull("(1) an instance of thrown exception must not be NULL", thrown); + assertNotNull("regular expression must not be null", regex); + final String msg = getMessage(thrown); + assertNotNull("observed exception which produced unacceptable NULL message", msg); + return regex.matchesSafely(msg); + } + + private static boolean isMatchingMessageByHamcrest(final Throwable thrown, final Matcher matchWith) { + assertNotNull("(3) an instance of thrown exception must not be NULL", thrown); + assertNotNull("hamcrest matcher must not be null", matchWith); + return matchWith.matches(getMessage(thrown)); + } + + @org.hamcrest.Factory + public static > IIsThrowingMessage throwing(final Class... expectedExceptions) { + return new IsThrowing(Operation.THROWING, true, false, expectedExceptions); + } + + @org.hamcrest.Factory + public static > IsThrowing throwing(final Matcher> expectedExceptions) { + return new IsThrowing(true, false, expectedExceptions); + } + + @org.hamcrest.Factory + public static > IsThrowing throwingDeeply(final Matcher> expectedExceptions) { + return new IsThrowing(true, true, expectedExceptions); + } + + @org.hamcrest.Factory + public static Matcher> throwing(final Matcher expectedExceptions) { + return new IsThrowing>(false, expectedExceptions); + } + + @org.hamcrest.Factory + public static Matcher> throwingDeeply(final Matcher expectedExceptions) { + return new IsThrowing>(true, expectedExceptions); + } + + @org.hamcrest.Factory + public static > IsThrowing notThrowing(final Class... exceptionsComplement) { + return new IsThrowing(Operation.NOT_THROWING, false, false, exceptionsComplement); + } + + @org.hamcrest.Factory + public static > IIsThrowingMessage throwingDeeply(final Class... expectedExceptions) { + return new IsThrowing(Operation.THROWING_DEEPLY, true, true, expectedExceptions); + } + + @org.hamcrest.Factory + public static > IsThrowing notThrowingDeeply(final Class... exceptionsComplement) {//vsetko + return new IsThrowing(Operation.NOT_THROWING_DEEPLY, false, true, exceptionsComplement); + } +} diff --git a/matchers/src/main/java/org/junit/contrib/matchers/MatchFlag.java b/matchers/src/main/java/org/junit/contrib/matchers/MatchFlag.java new file mode 100644 index 0000000..cf4b839 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/MatchFlag.java @@ -0,0 +1,72 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +import java.util.regex.Pattern; + +/** + * The purpose of this enum type is to declare Pattern integer constants in this num form. + *

See Equivalent Embedded Flag Expression in {@link java.util.regex.Pattern}. + * @author Tibor17 + * @version 0.1 + * @see Pattern + * @since 0.1, Oct 2, 2011, 7:38:45 PM + */ +public enum MatchFlag { + UNIX_LINES("(?d)", Pattern.UNIX_LINES), + CASE_INSENSITIVE("(?i)", Pattern.CASE_INSENSITIVE), + COMMENTS("(?x)", Pattern.COMMENTS), + MULTILINE("(?m)", Pattern.MULTILINE), + LITERAL(null, Pattern.LITERAL), + DOTALL("(?s)", Pattern.DOTALL), + UNICODE_CASE("(?u)", Pattern.UNICODE_CASE), + CANON_EQ(null, Pattern.CANON_EQ); + + private final int flag; + private final String expression; + + private MatchFlag(String expression, int flag) { + this.flag = flag; + this.expression = expression; + } + + /** + * Returns embedded flag expressions for this constant; or null if undefined. + * The embedded flag expressions that correspond to these constants are the following: + *

+ *

+ * @return embedded flag expressions for this constant; or null if undefined + */ + public String getEmbeddedFlagExpression() { + return expression; + } + + int getJavaFlag() { + return flag; + } + + @Override + public String toString() { + return expression == null ? name() : expression; + } +} \ No newline at end of file diff --git a/matchers/src/main/java/org/junit/contrib/matchers/Position.java b/matchers/src/main/java/org/junit/contrib/matchers/Position.java new file mode 100644 index 0000000..e39692c --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/Position.java @@ -0,0 +1,78 @@ +package org.junit.contrib.matchers; + +import org.hamcrest.BaseMatcher; +import org.hamcrest.Description; +import org.hamcrest.Matcher; + +import static org.junit.Assert.assertNotNull; + +/** + * Skeleton class for {@link StartPosition} and {@link EndPosition}. + * Determines a position index + * (as a result of {@link java.util.regex.Matcher#start(int)} or {@link java.util.regex.Matcher#end(int)}) + * been matched with the given hamcrest's matcher expectedIndex as expected one. + *

+ * This is performed by {@link #matchExpectedIndex(int)}. The mismatched result enables the caller + * to retrieve failure description via {@link #getFailureDescription()} after method the + * {@link #matchExpectedIndex(int)} returned {@code false}. + *

+ * The method {@link #isEvaluated()} can be called after {@link #matchExpectedIndex(int)}. Thus the method + * {@link #isEvaluated()} always returns {@code true} if, and only if, {@link #matchExpectedIndex(int)} was + * called before with whatever value it returned. + */ +abstract class Position { + protected final ExpectedMatcher expectedIndex; + protected final int group; + private String failureDescription; + + protected abstract boolean isEvaluated(); + + static final class ExpectedMatcher extends BaseMatcher { + private final Matcher expectedIndex; + private boolean isExpectedIndexMatched, isExpectedIndexEvaluated; + + private ExpectedMatcher(Matcher expectedIndex) { + assertNotNull("given position matcher must not be null", expectedIndex); + this.expectedIndex = expectedIndex; + } + + @Override public boolean matches(Object o) { + isExpectedIndexEvaluated = true; + return isExpectedIndexMatched = expectedIndex.matches(o); + } + + boolean isExpectedIndexEvaluated() { + return isExpectedIndexEvaluated; + } + + boolean isExpectedIndexMatched() { + return isExpectedIndexMatched; + } + + @Override + public void describeTo(Description description) { + expectedIndex.describeTo(description); + } + } + + Position(int group, Matcher expectedIndex) { + this.group = group; + this.expectedIndex = new ExpectedMatcher(expectedIndex); + } + + protected final boolean matchExpectedIndex(int actualIndex) { + final boolean matches = expectedIndex.matches(actualIndex); + if (!matches) failureDescription = failureAfterMatch(); + return matches; + } + + protected final String getFailureDescription() { + return failureDescription; + } + + private String failureAfterMatch() { + final Description description = new InternalDescription(); + expectedIndex.describeTo(description); + return description.toString(); + } +} \ No newline at end of file diff --git a/matchers/src/main/java/org/junit/contrib/matchers/RegexProperties.java b/matchers/src/main/java/org/junit/contrib/matchers/RegexProperties.java new file mode 100644 index 0000000..958e5bc --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/RegexProperties.java @@ -0,0 +1,212 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +import java.util.Arrays; + +import static org.junit.Assert.fail; +import static org.junit.Assert.assertNotNull; + +/** + * The purpose of this class is to specify properties on static methods like + * and match in {@link org.junit.contrib.matchers.IsRegex}. + * As for instance, the input sequence "aAaBbcD" is handled and other case-insensitive + * subsequence within the region [1, 6) is to be matched with the pattern "a*b". + * This means that the subsequence "AaBbc" should be considered.

+ *

+ *      RegexProperties regexProperties = new RegexProperties(CASE_INSENSITIVE).setStartRegion(1).setEndRegion(6);
+ *      assertThat("aAaBbcD", is(like("a*b", regexProperties)));
+ * 
+ *

+ * @author tibor17 + * @version 0.1 + * @see org.junit.contrib.matchers.IsRegex + * @since 0.1, 27.12.2011, 17:57 + */ +public final class RegexProperties { + private int startRegion, endRegion = -1; + private MatchFlag[] flags = new MatchFlag[0]; + private boolean useAnchoringBounds = true, useTransparentBounds; + + /** + * Default properties. + */ + public RegexProperties() {} + + /** + * Properties with arbitrary mach flags. + * @param matchFlags mach flags array + * @throws AssertionError + * if the array is null or contains null element(s) + * @see java.util.regex.Pattern#compile(String, int) + */ + public RegexProperties(MatchFlag... matchFlags) { + setMatchFlags(matchFlags); + } + + /** + * Specifies the start index on the input sequence been applicable to {@link IsRegex}. + * @param startRegion start index on the input sequence + * @return this + * @throws AssertionError + * if negative number startRegion + * @see java.util.regex.Matcher#region(int, int) + */ + public RegexProperties setStartRegion(int startRegion) { + if (startRegion < 0) + fail("startRegion is negative"); + this.startRegion = startRegion; + return this; + } + + /** + * Specifies the end index on the input sequence been applicable to {@link IsRegex}. + * @param endRegion end index on the input sequence + * @return this + * @throws AssertionError + * if negative number endRegion + * @see java.util.regex.Matcher#region(int, int) + */ + public RegexProperties setEndRegion(int endRegion) { + if (endRegion < 0) + fail("endRegion is negative"); + this.endRegion = endRegion; + return this; + } + + /** + * Properties with arbitrary mach flags. + * @param matchFlags mach flags array + * @return this + * @throws AssertionError + * if the array is null or contains null element(s) + * @see java.util.regex.Pattern#compile(String, int) + */ + public RegexProperties setMatchFlags(MatchFlag... matchFlags) { + if (matchFlags == null) + fail("null flags on given array"); + MatchFlag[] flags = new MatchFlag[matchFlags.length]; + int i = 0; + for (MatchFlag flag : matchFlags) { + if (flag == null) + fail("null element at " + i + + " in given flags array " + + Arrays.toString(matchFlags)); + if (!hasSameInstance(0, i, flag, flags)) flags[i++] = flag; + } + this.flags = i == flags.length ? flags : Arrays.copyOf(flags, i); + return this; + } + + /** + * Sets the anchoring of region bounds for this matcher. + * @param enable a boolean indicating whether or not to use anchoring bounds + * @return this + * @see java.util.regex.Matcher#useAnchoringBounds(boolean) + */ + public RegexProperties setAnchoringBounds(boolean enable) { + useAnchoringBounds = enable; + return this; + } + + /** + * Sets the transparency of region bounds for this matcher. + * @param enable a boolean indicating whether to use opaque or transparent regions + * @return this + * @see java.util.regex.Matcher#useTransparentBounds(boolean) + */ + public RegexProperties setTransparentBounds(boolean enable) { + useTransparentBounds = enable; + return this; + } + + /** + * A value provided by {@link #setStartRegion(int)}; or zero otherwise (by default). + * @return {@link #setStartRegion(int)}; or zero otherwise (by default) + * @see java.util.regex.Matcher#region(int, int) + */ + public int getStartRegion() { + return startRegion; + } + + /** + * A value provided by {@link #setEndRegion(int)}; or -1 otherwise (unlimited by default). + * @return {@link #setEndRegion(int)}; or -1 otherwise (unlimited by default) + * @see java.util.regex.Matcher#region(int, int) + */ + public int getEndRegion() { + return endRegion; + } + + /** + * Properties with arbitrary mach flags. + * @return mach flags + * @see java.util.regex.Pattern#compile(String, int) + */ + public MatchFlag[] getFlags() { + return flags.clone(); + } + + /** + * Returns a boolean indicating whether or not to use anchoring bounds ({@code true} by default). + * @return a boolean indicating whether or not to use anchoring bounds ({@code true} by default) + * @see java.util.regex.Matcher#useAnchoringBounds(boolean) + */ + public boolean hasAnchoringBounds() { + return useAnchoringBounds; + } + + /** + * Returns a boolean indicating whether to use opaque or transparent regions ({@code false} by default). + * @return a boolean indicating whether to use opaque or transparent regions ({@code false} by default) + * @see java.util.regex.Matcher#useTransparentBounds(boolean) + */ + public boolean hasTransparentBounds() { + return useTransparentBounds; + } + + /** + * Returns {@code true} if, and only if, the array references + * has the given reference within given range of the array indexes. + * @param references content + * @param from inclusive + * @param to exclusive + * @param reference a reference to find + * @param generic parameter of both the given reference and the array + * @return {@code true} if found reference between from (inclusive) and to (exclusive) + * @throws AssertionError if {@code from > to} + * ; or if {@code from < 0 or to > references.length} + * ; or if null references + */ + private static boolean hasSameInstance(final int from, final int to, final T reference, final T... references) { + assertNotNull("references: null", references); + + if (from < 0 || to > references.length) + fail("from: " + from + + ", to: " + to + + ", references.length: " + references.length); + + if (from > to) + fail("from: " + from + + ", to: " + to); + + for (int i = from; i < to; ++i) { + if (references[i] == reference) + return true; + } + + return false; + } +} diff --git a/matchers/src/main/java/org/junit/contrib/matchers/RegexResult.java b/matchers/src/main/java/org/junit/contrib/matchers/RegexResult.java new file mode 100644 index 0000000..ce9f86f --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/RegexResult.java @@ -0,0 +1,96 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +import static org.junit.contrib.matchers.IsRegexResult.ANY_GROUP; + +/** + * The purpose of this immutable object is to wrap information (group, start, end) + * of successful match after using a regular expression on an input (sub)sequence. + *

+ * @author tibor17 + * @version 0.1 + * @see org.junit.contrib.matchers.IsRegexResult + * @since 0.1, 26.12.2011, 11:21 + */ +final public class RegexResult { + /** + * Results. + */ + private final int group, start, end; + + /** + * Encapsulates a region of match returned by methods {@link java.util.regex.Matcher#start(int)} and + * {@link java.util.regex.Matcher#end(int)} per group which is -1 or within the + * range 0 and {@link java.util.regex.Matcher#groupCount()}. + *

+ * If the group is -1, then any group of group's range is possibly used for comparison in + * {@link IsRegexResult} Hamcrest matcher. + * @param group group for start or end operation (must be non-negative or -1) + * @param start + * start index (must be non-negative) of the subsequence captured by the given group on a match operation + * @param end + * end index (must be non-negative) of the subsequence captured by the given group on a match operation + * @throws IllegalArgumentException + * if group is less than -1; + * or if stat or end is negative + */ + RegexResult(int group, int start, int end) { + if (group < ANY_GROUP) throw new IllegalArgumentException("group is " + group); + if (start < 0) throw new IllegalArgumentException("start is " + start); + if (end < 0) throw new IllegalArgumentException("end is " + end); + this.group = group; + this.start = start; + this.end = end; + } + + /** + * Returns the value passed in by the constructor. + * @return group. Must be non-negative number when passed in by {@link #RegexResult(int, int, int)} constructor} + * ; otherwise -1 which appears any group when passed in by + * {@link #RegexResult(int, int, int)}. + */ + public int getGroup() { + return group; + } + + /** + * Returns the value passed in by the constructor. + * @return (a non-negative number of) start operation + */ + public int getStart() { + return start; + } + + /** + * Returns the value passed in by the constructor. + * @return (a non-negative number of) end operation + */ + public int getEnd() { + return end; + } + + @Override + public int hashCode() { + return start ^ end ^ group; + } + + @Override + public boolean equals(Object o) { + if (o == null || !(o instanceof RegexResult)) return false; + final RegexResult rr = (RegexResult) o; + return rr.start == start && rr.end == end && rr.group == group; + } +} diff --git a/matchers/src/main/java/org/junit/contrib/matchers/ResultType.java b/matchers/src/main/java/org/junit/contrib/matchers/ResultType.java new file mode 100644 index 0000000..ef24cd6 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/ResultType.java @@ -0,0 +1,27 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +/** + * The purpose of this private package enum is to specify + * four post operations callable after successful regular + * expression match in {@link IsRegex}. + *

+ * @author tibor17 + * @version 0.1 + * @see org.junit.contrib.matchers.IsRegex + * @since 0.1, 26.12.2011, 11:22 + */ +enum ResultType { GROUP, START, END, REGION } diff --git a/matchers/src/main/java/org/junit/contrib/matchers/StartPosition.java b/matchers/src/main/java/org/junit/contrib/matchers/StartPosition.java new file mode 100644 index 0000000..0ae1a23 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/StartPosition.java @@ -0,0 +1,45 @@ +package org.junit.contrib.matchers; + +import org.hamcrest.Matcher; + +/** + * Determines whether the {@link java.util.regex.Matcher} has start position matching + * the regex criteria by {@link #start(java.util.regex.Matcher)}. In prior the caller + * must satisfy the call {@link java.util.regex.Matcher#find(int)}. + *

+ * If a failure appears after calling {@link #start(java.util.regex.Matcher)}, then the + * method {@link #hasFailure()} returns {@code true} and the failure description is + * retrieved by the return from {@link #getFailureDescription()}. + */ +final class StartPosition extends Position { + private int start = -1; + private boolean matches, isEvaluated; + + @Override + protected boolean isEvaluated() { + return isEvaluated; + } + + StartPosition(int group, Matcher expectedIndex) { + super(group, expectedIndex); + } + + boolean start(final java.util.regex.Matcher regexMatcher) { + isEvaluated = true; + try { + start = regexMatcher.start(group); + matches = start != -1 && matchExpectedIndex(start); + } catch (IllegalStateException e) { + matches = false; + } + return matches; + } + + boolean hasFailure() { + return !matches && getFailureDescription() != null; + } + + @Override public String toString() { + return start == -1 ? "" : "(per group " + group + ") from start position " + start; + } +} \ No newline at end of file diff --git a/matchers/src/main/java/org/junit/contrib/matchers/ThrownMessageMatcher.java b/matchers/src/main/java/org/junit/contrib/matchers/ThrownMessageMatcher.java new file mode 100644 index 0000000..6644327 --- /dev/null +++ b/matchers/src/main/java/org/junit/contrib/matchers/ThrownMessageMatcher.java @@ -0,0 +1,84 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +import org.hamcrest.BaseDescription; +import org.hamcrest.Matcher; + +/** + * The purpose of this class is an internal use in {@link IsThrowing}, when combining expected exception to throw by + * a block of code together with a certain message matching the expectations of the matcher {@link IsThrowing}. + * @author Tibor17 + * @version 0.1 + * @see IsThrowing + * @since 0.1, Sep 28, 2011, 11:26:00 AM + */ +abstract class ThrownMessageMatcher { + protected final String beforeNullActual, beforeActual, afterActual, + beforeExpected, afterExpected, + expectedMatcherPrefix; + + protected ThrownMessageMatcher(String beforeNullActual, String beforeActual, String afterActual, + String beforeExpected, String afterExpected, + String expectedMatcherPrefix) { + this.beforeNullActual = beforeNullActual; + this.beforeActual = beforeActual; + this.afterActual = afterActual; + this.beforeExpected = beforeExpected; + this.afterExpected = afterExpected; + this.expectedMatcherPrefix = expectedMatcherPrefix; + } + + protected abstract boolean isMatching(Throwable thrown); + protected abstract boolean hasNullMessage(Throwable thrown); + protected abstract String getActualMessage(Throwable thrown); + protected abstract Matcher getExpectedMatcher(); + protected abstract String getExpectedMessage(); + + final String createErrorMessage(final Throwable thrown) { + final StringBuilder msg = new StringBuilder(128); + + if (hasNullMessage(thrown)) msg.append(beforeNullActual); + else msg.append(beforeActual).append(getActualMessage(thrown)).append(afterActual); + msg.append(beforeExpected); + + final Matcher expectedMatcher = getExpectedMatcher(); + + if (expectedMatcher != null) { + final int insertAt = msg.length(); + final String shouldStartWith = expectedMatcherPrefix; + final boolean[] startsThat = {false}; + + expectedMatcher.describeTo(new BaseDescription() { + private int i, upTo = shouldStartWith.length(); + @Override protected void append(char c) { + if (i < upTo && (i == 0 || startsThat[0])) + startsThat[0] = c == shouldStartWith.charAt(i); + ++i; + msg.append(c); + } + @Override public String toString() { return msg.toString(); } + }); + + if (!startsThat[0]) + msg.insert(insertAt, shouldStartWith); + + } else msg.append(getExpectedMessage()); + + msg.append(afterExpected); + + return msg.toString(); + } +} \ No newline at end of file diff --git a/matchers/src/test/java/org/junit/contrib/matchers/TestException.java b/matchers/src/test/java/org/junit/contrib/matchers/TestException.java new file mode 100644 index 0000000..5e76ecb --- /dev/null +++ b/matchers/src/test/java/org/junit/contrib/matchers/TestException.java @@ -0,0 +1,54 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +import org.junit.Ignore; + +/** + * This is test exception used rather than nested class which naming is totally compiler specific. + *

+ * + * @author tibor17 + * @version 0.1 + * @see org.junit.contrib.matchers.IsRegex + * @since 0.1, 24.3.2012, 22:53 + */ +@Ignore +class TestException extends Exception { + private final String localizedMessage; + + TestException(String message, String localizedMessage) { + super(message); + this.localizedMessage = localizedMessage; + } + + TestException(String message) { + super(message); + localizedMessage = message; + } + + TestException(String message, Throwable cause) { + super(message, cause); + localizedMessage = message; + } + + TestException(Throwable cause) { + super(cause); + localizedMessage = getMessage(); + } + + public + @Override String getLocalizedMessage() { return localizedMessage; } +} diff --git a/matchers/src/test/java/org/junit/contrib/matchers/TestIsAssignableTo.java b/matchers/src/test/java/org/junit/contrib/matchers/TestIsAssignableTo.java new file mode 100644 index 0000000..06a0524 --- /dev/null +++ b/matchers/src/test/java/org/junit/contrib/matchers/TestIsAssignableTo.java @@ -0,0 +1,145 @@ +package org.junit.contrib.matchers; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.io.IOException; +import java.io.NotSerializableException; + +import static org.hamcrest.core.Is.is; +import static org.junit.Assert.assertThat; +import static org.junit.contrib.matchers.IsAssignableTo.*; + +/** + * The purpose of this test is to test a functionality of + * {@link IsAssignableTo} matcher and to explain the use. + *

+ * @author Tibor17 + * @version 0.1 + * @see IsAssignableTo + * @since 0.1, Dec 11, 2011, 7:13:24 PM + */ +public final class TestIsAssignableTo { + @Rule + public final ExpectedException expectedExceptionRule = ExpectedException.none(); + + final class Sub extends SuperClass implements SuperInterface {} + class SuperClass extends SuperBaseClass {} + interface SuperInterface extends SuperBaseInterface {} + class SuperBaseClass {} + interface SuperBaseInterface {} + final class OtherClass {} + interface OtherInterface {} + + @Test + public void isAssignableToSame() { + assertThat(Sub.class, is(assignableTo(Sub.class))); + } + + @Test @SuppressWarnings("unchecked") + public void isAssignableToExpectedSuperInterface$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("is assignable to org.junit.contrib.matchers.TestIsAssignableTo$Sub"); + + Class actual = SuperInterface.class; + Class expected = Sub.class; + assertThat(actual, is(assignableTo(expected))); + } + + @Test @SuppressWarnings("unchecked") + public void isAssignableToExpectedSuperInterface() { + Class actual = Sub.class; + Class expected = SuperInterface.class; + assertThat(actual, is(assignableTo(expected))); + } + + @Test @SuppressWarnings("unchecked") + public void isAssignableToExpectedSuperBaseInterface$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("is assignable to org.junit.contrib.matchers.TestIsAssignableTo$Sub"); + + Class actual = SuperBaseInterface.class; + Class expected = Sub.class; + assertThat(actual, is(assignableTo(expected))); + } + + @Test @SuppressWarnings("unchecked") + public void isAssignableToExpectedSuperBaseInterface() { + Class actual = Sub.class; + Class expected = SuperBaseInterface.class; + assertThat(actual, is(assignableTo(expected))); + } + // + + @Test + public void isAssignableToAnyOtherInterface$NegativeTest$1() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("is assignable to org.junit.contrib.matchers.TestIsAssignableTo$OtherInterface"); + + assertThat(OtherClass.class, is(assignableToAny(OtherInterface.class))); + } + + @Test + public void isAssignableToAnyOtherClass$NegativeTest$2() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("is assignable to org.junit.contrib.matchers.TestIsAssignableTo$OtherClass"); + + assertThat(OtherInterface.class, is(assignableToAny(OtherClass.class))); + } + + @Test + public void isAssignableToAnyOtherClass$NegativeTest$3() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("is assignable to org.junit.contrib.matchers.TestIsAssignableTo$OtherClass"); + + assertThat(Sub.class, is(assignableToAny(OtherClass.class))); + } + + @Test + public void isAssignableToAnyOtherInterface$NegativeTest$4() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("is assignable to org.junit.contrib.matchers.TestIsAssignableTo$OtherInterface"); + + assertThat(Sub.class, is(assignableToAny(OtherInterface.class))); + } + + @Test + public void isAssignableToAnyExpectedSuperClass() { + assertThat(Sub.class, is(assignableToAny(SuperClass.class))); + } + + @Test + public void isAssignableToAnyExpectedSuperInterface() { + assertThat(Sub.class, is(assignableToAny(SuperInterface.class))); + } + + @Test + public void isAssignableToAnyExpectedSuperBaseClass() { + assertThat(Sub.class, is(assignableToAny(SuperBaseClass.class))); + } + + @Test + public void isAssignableToAnyExpectedSuperBaseInterface() { + assertThat(Sub.class, is(assignableToAny(SuperBaseInterface.class))); + } + // + + @Test + public void isAssignableToThrowable$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("is assignable to java.io.NotSerializableException"); + + assertThat(IOException.class, is(assignableToThrowable(NotSerializableException.class))); + } + + @Test + public void isAssignableToThrowable$1() { + assertThat(NotSerializableException.class, is(assignableToThrowable(IOException.class))); + } + + @Test + public void isAssignableToThrowable$2() { + assertThat(IOException.class, is(assignableToThrowable(IOException.class))); + } +} diff --git a/matchers/src/test/java/org/junit/contrib/matchers/TestIsRegex.java b/matchers/src/test/java/org/junit/contrib/matchers/TestIsRegex.java new file mode 100644 index 0000000..46ec676 --- /dev/null +++ b/matchers/src/test/java/org/junit/contrib/matchers/TestIsRegex.java @@ -0,0 +1,590 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import static org.hamcrest.core.AnyOf.anyOf; +import static org.hamcrest.core.AllOf.allOf; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.junit.Assert.assertThat; +import static org.junit.contrib.matchers.IsRegex.like; +import static org.junit.contrib.matchers.IsRegex.match; +import static org.junit.contrib.matchers.IsRegexResult.*; +import static org.junit.contrib.matchers.MatchFlag.*; + +/** + * The purpose of this test is to test a functionality of + * {@link IsRegex} matcher and to explain the use. + *

+ * @author Tibor17 + * @version 0.1 + * @see IsRegex + * @since 0.1, Oct 11, 2011, 7:24:21 PM + */ +public final class TestIsRegex { + @Rule + public final ExpectedException expectedExceptionRule = ExpectedException.none(); + + @Test + public void baseLike$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"hello|hi\"\n" + + " got: \"Hi There!\""); + assertThat("Hi There!", is(like("hello|hi"))); + } + + @Test + public void baseLike() { + assertThat("Hi There!", is(like("Hello|Hi"))); + } + + @Test + public void likeCaseInsensitive$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"hello|hi\"\n" + + " got: \"Hi There!\""); + assertThat("Hi There!", is(like("hello|hi", new RegexProperties()))); + } + + @Test + public void likeCaseInsensitive() { + assertThat("Hi There!", is(like("hello|hi", new RegexProperties(CASE_INSENSITIVE)))); + } + + @Test + public void likeCaseInsensitiveStart$1$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"(?i)hello|hi\" " + + "starts at <1>\n" + + " got: \"Hi There!\""); + assertThat("Hi There!", is(like("hello|hi", new RegexProperties(CASE_INSENSITIVE)).startsAt(1))); + } + + @Test + public void likeCaseInsensitiveStart$1() { + assertThat("Hi There!", is(like("hello|hi", new RegexProperties(CASE_INSENSITIVE)).startsAt(0))); + } + + @Test + public void likeCaseInsensitiveEnd$1$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"(?i)hello|hi\" " + + "ends at <1>\n got: \"Hi There!\""); + assertThat("Hi There!", is(like("hello|hi", new RegexProperties(CASE_INSENSITIVE)).endsAt(1))); + } + + @Test + public void likeCaseInsensitiveEnd$1() { + assertThat("Hi There!", is(like("hello|hi", new RegexProperties(CASE_INSENSITIVE)).endsAt(2))); + } + + @Test + public void likeCaseInsensitiveStartEnd$1$NegativeTest1() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"(?i)Hello|Hi\" " + + "starts at <1> and ends at <1>\n" + + " got: \"Hi There!\""); + assertThat("Hi There!", is(like("Hello|Hi", new RegexProperties(CASE_INSENSITIVE)).startsAt(1).endsAt(1))); + } + + @Test + public void likeCaseInsensitiveStartEnd$1$NegativeTest2() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("not applicable when result matcher already specified"); + assertThat("Hi There!", is(like("Hello|Hi", new RegexProperties(CASE_INSENSITIVE)).startsAt(0).endsAt(2).startsAt(0))); + } + + @Test + public void likeCaseInsensitiveStartEnd$1() { + assertThat("Hi There!", is(like("Hello|Hi", new RegexProperties(CASE_INSENSITIVE)).startsAt(0).endsAt(2))); + } + + @Test + public void likeLogicalOR() { + assertThat("Hi There!\nHello ...\nWhat's up!", is(like("What's up|Hello|Hi"))); + } + + @Test + public void likeLogicalOR$NegativeTest$1() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"What's up|Hello|Hi\" " + + "starts at <11> and ends at (<28> or <16>)\n" + + " got: \"Hi There!\\nHello ...\\nWhat's up!\""); + + assertThat("Hi There!\nHello ...\nWhat's up!", + is(like("What's up|Hello|Hi") + .startsAt(equalTo(11)) + .endsAt(anyOf(equalTo(28), equalTo(16))))); + } + + @Test + public void likeLogicalOR$NegativeTest$2() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"What's up|Hello|Hi\" " + + "ends at (<28> or <16>)\n" + + " got: \"Hi There!\\nHello ...\\nWhat's up!\""); + + assertThat("Hi There!\nHello ...\nWhat's up!", + is(like("What's up|Hello|Hi") + .startsAt(equalTo(10)) + .endsAt(anyOf(equalTo(28), equalTo(16))))); + } + + @Test + public void likeLogicalOR$NegativeTest$3() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"What's up|Hello|Hi\" " + + "ends at (<28> or <16>)\n" + + " got: \"Hi There!\\nHello ...\\nWhat's up!\""); + + assertThat("Hi There!\nHello ...\nWhat's up!", + is(like("What's up|Hello|Hi") + .endsAt(anyOf(equalTo(28), equalTo(16))))); + } + + @Test + public void likeLogicalOR$NegativeTest$4() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"What's up|Hello|Hi\" " + + "starts at <11>\n" + + " got: \"Hi There!\\nHello ...\\nWhat's up!\""); + + assertThat("Hi There!\nHello ...\nWhat's up!", + is(like("What's up|Hello|Hi") + .startsAt(equalTo(11)) + .endsAt(anyOf(equalTo(28), equalTo(15))))); + } + + @Test + public void likeLogicalOR$NegativeTest$5() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"What's up|Hello|Hi\" " + + "starts at <11>\n" + + " got: \"Hi There!\\nHello ...\\nWhat's up!\""); + + assertThat("Hi There!\nHello ...\nWhat's up!", + is(like("What's up|Hello|Hi") + .startsAt(equalTo(11)))); + } + + @Test + public void likeLogicalOR$1() { + assertThat("Hi There!\nHello ...\nWhat's up!", + is(like("What's up|Hello|Hi") + .startsAt(10).endsAt(15))); + } + + @Test + public void likeLogicalOR$2() { + assertThat("Hi There!\nHello ...\nWhat's up!", + is(like("What's up|Hello|Hi") + .startsAt(equalTo(10)) + .endsAt(anyOf(equalTo(29), equalTo(15))))); + } + + @Test + public void likeLogicalORandGroup() { + assertThat("hat is the hit", is(like("h(a|i)t"))); + } + + @Test + public void likeLogicalORandGroup$1$1() { + assertThat("hat is the hit", is(like("h(a|i)t").startsAt(0, 0).endsAt(0, 3))); + } + + @Test + public void likeLogicalORandGroup$1$2NegativeTest$1$1() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"h(a|i)t\" " + + "on region from <0> to <3> group 1\n" + + " got: \"hat is the hit\""); + + assertThat("hat is the hit", is(like("h(a|i)t", regionByGroup(1, equalTo(0), equalTo(3))))); + } + + @Test + public void likeLogicalORandGroup$1$2NegativeTest$1$2() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"h(a|i)t\" " + + "on region from <0> to <3> group 1\n" + + " got: \"hat is the hit\""); + + assertThat("hat is the hit", is(like("h(a|i)t", regionByGroup(1, 0, 3)))); + } + + @Test + public void likeLogicalORandGroup$1$2NegativeTest$2$1() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"h(a|i)t\" " + + "on region from <1> to <3> group 0\n" + + " got: \"hat is the hit\""); + + assertThat("hat is the hit", is(like("h(a|i)t", regionByGroup(0, equalTo(1), equalTo(3))))); + } + + @Test + public void likeLogicalORandGroup$1$2NegativeTest$2$2() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"h(a|i)t\" " + + "on region from <1> to <3> group 0\n" + + " got: \"hat is the hit\""); + + assertThat("hat is the hit", is(like("h(a|i)t", regionByGroup(0, 1, 3)))); + } + + @Test + public void likeLogicalORandGroup$1$2NegativeTest$3$1() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"h(a|i)t\" " + + "on region from <0> to <2> group 0\n" + + " got: \"hat is the hit\""); + + assertThat("hat is the hit", is(like("h(a|i)t", regionByGroup(0, equalTo(0), equalTo(2))))); + } + + @Test + public void likeLogicalORandGroup$1$2NegativeTest$3$2() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"h(a|i)t\" " + + "on region from <0> to <2> group 0\n" + + " got: \"hat is the hit\""); + + assertThat("hat is the hit", is(like("h(a|i)t", regionByGroup(0, 0, 2)))); + } + + @Test + public void likeLogicalORandGroup$1$2$1() { + assertThat("hat is the hit", is(like("h(a|i)t", regionByGroup(0, equalTo(0), equalTo(3))))); + } + + @Test + public void likeLogicalORandGroup$1$2$2() { + assertThat("hat is the hit", is(like("h(a|i)t", regionByGroup(0, 0, 3)))); + } + + @Test + public void likeLogicalORandGroup$2$1() { + assertThat("hat is the hit", is(like("h(a|i)t").startsAt(0, 11).endsAt(0, 14))); + } + + @Test + public void likeLogicalORandGroup$2$2() { + assertThat("hat is the hit", is(like("h(a|i)t", regionByGroup(0, equalTo(11), equalTo(14))))); + } + + @Test + public void likeLogicalORandGroup$2$3() { + assertThat("hat is the hit", is(like("h(a|i)t", regionByGroup(0, 11, 14)))); + } + + @Test + public void likeLogicalORandGroup$3$1() { + assertThat("hat is the hit", is(like("h(a|i)t").startsAt(1, 1).endsAt(1, 2))); + } + + @Test + public void likeLogicalORandGroup$3$2() { + assertThat("hat is the hit", is(like("h(a|i)t", regionByGroup(1, equalTo(1), equalTo(2))))); + } + + @Test + public void likeLogicalORandGroup$3$3() { + assertThat("hat is the hit", is(like("h(a|i)t", regionByGroup(1, 1, 2)))); + } + + @Test + public void likeLogicalORandRegionInAnyGroup$1NegativTest$1() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"h(a|i)t\" " + + "on region from <2> to <2>\n" + + " got: \"hat is the hit\""); + + assertThat("hat is the hit", is(like("h(a|i)t", region(equalTo(2), equalTo(2))))); + } + + @Test + public void likeLogicalORandRegionInAnyGroup$1NegativTest$2() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"h(a|i)t\" " + + "on region from <1> to <3>\n" + + " got: \"hat is the hit\""); + + assertThat("hat is the hit", is(like("h(a|i)t", region(equalTo(1), equalTo(3))))); + } + + @Test + public void likeLogicalORandRegionInAnyGroup$1() { + assertThat("hat is the hit", is(like("h(a|i)t", region(equalTo(1), equalTo(2))))); + } + + @Test + public void likeLogicalORandRegionInAnyGroup$2NegativTest$1() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"h(a|i)t\" " + + "on region from <2> to <2>\n" + + " got: \"hat is the hit\""); + + assertThat("hat is the hit", is(like("h(a|i)t", region(2, 2)))); + } + + @Test + public void likeLogicalORandRegionInAnyGroup$2NegativTest$2() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"h(a|i)t\" " + + "on region from <1> to <3>\n" + + " got: \"hat is the hit\""); + + assertThat("hat is the hit", is(like("h(a|i)t", region(1, 3)))); + } + + @Test + public void likeLogicalORandRegionInAnyGroup$2() { + assertThat("hat is the hit", is(like("h(a|i)t", region(1, 2)))); + } + + @Test + public void groupsCombination$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"ab(b)a\" " + + "on group <2>\n" + + " got: \"abba\""); + + assertThat("abba", is(like("ab(b)a", group(equalTo(2))))); + } + + @Test + public void groupsCombination() { + assertThat("abba", is(like("ab(b)a", group(anyOf(equalTo(1), equalTo(0)))))); + } + + @Test + public void twoStartsWithLike() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("not applicable when result matcher already specified"); + assertThat("", is(like("").startsAt(0).endsAt(0).startsAt(0))); + } + + @Test + public void twoEndsWithLike() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("not applicable when result matcher already specified"); + assertThat("", is(like("").endsAt(0).startsAt(0).endsAt(0))); + } + + @Test + public void likeCaseInsensitiveStart$2$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"(?i)hello|hi\" " + + "on start position <1>\n" + + " got: \"Hi There!\""); + assertThat("Hi There!", is(like("hello|hi", new RegexProperties(CASE_INSENSITIVE), start(1)))); + } + + @Test + public void likeCaseInsensitiveStart$2() { + assertThat("Hi There!", is(like("hello|hi", new RegexProperties(CASE_INSENSITIVE), start(0)))); + } + + @Test + public void likeCaseInsensitiveEnd$2$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"(?i)hello|hi\" on " + + "end position <1>\n" + + " got: \"Hi There!\""); + assertThat("Hi There!", is(like("hello|hi", new RegexProperties(CASE_INSENSITIVE), end(1)))); + } + + @Test + public void likeCaseInsensitiveEnd$2() { + assertThat("Hi There!", is(like("hello|hi", new RegexProperties(CASE_INSENSITIVE), end(2)))); + } + + @Test + public void likeCaseInsensitiveStartEnd$2$NegativeTest1() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is like \"(?i)Hello|Hi\" " + + "on (start position <1> and end position <1>)\n" + + " got: \"Hi There!\""); + assertThat("Hi There!", is(like("Hello|Hi", new RegexProperties(CASE_INSENSITIVE), allOf(start(1), end(1))))); + } + + @Test + public void likeCaseInsensitiveStartEnd$2() { + assertThat("Hi There!", is(like("Hello|Hi", new RegexProperties(CASE_INSENSITIVE), allOf(start(0), end(2))))); + } + + @Test + public void likeLogicalORandGroup$4$1() { + assertThat("hat is the hit", is(like("h(a|i)t", + allOf(startByGroup(1, 1), endByGroup(1, 2))))); + } + + @Test + public void likeLogicalORandGroup$4$2() { + assertThat("hat is the hit", is(like("h(a|i)t", + allOf(startByGroup(0, 0), endByGroup(0, 3), + startByGroup(0, 11), endByGroup(0, 14), + startByGroup(1, 1), endByGroup(1, 2))))); + } + + @Test + public void baseMatch$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: match \"hello\"\n" + + " got: \" Hello \""); + assertThat(" Hello ", match("hello")); + } + + @Test + public void baseMatch() { + assertThat("hello", match("hello")); + } + + @Test + public void matchCaseInsensitive$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: match \"(?i)hello\"\n" + + " got: \" Hello \""); + assertThat(" Hello ", match("hello", new RegexProperties(CASE_INSENSITIVE))); + } + + @Test + public void matchCaseInsensitive() { + assertThat("Hello", match("hello", new RegexProperties(CASE_INSENSITIVE))); + } + + @Test + public void matchCaseInsensitiveStart() { + assertThat("Hello", match("hello", new RegexProperties(CASE_INSENSITIVE)) + .startsAt(0)); + } + + @Test + public void matchCaseInsensitiveEnd() { + assertThat("Hello", match("hello", new RegexProperties(CASE_INSENSITIVE)) + .endsAt(5)); + } + + @Test + public void matchCaseInsensitiveStartEnd$NegativeTest$1() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: match \"(?i)hello\" " + + "ends at <4> in group 0\n" + + " got: \"Hello\""); + assertThat("Hello", match("hello", new RegexProperties(CASE_INSENSITIVE)) + .startsAt(0).endsAt(4)); + } + + @Test + public void matchCaseInsensitiveStartEnd$NegativeTest$2() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: match \"(?i)hello\" starts at <1>\n" + + " got: \"Hello\""); + assertThat("Hello", match("hello", new RegexProperties(CASE_INSENSITIVE)) + .startsAt(1).endsAt(5)); + } + + @Test + public void matchCaseInsensitiveStartEnd$NegativeTest$3() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: match \"(?i)hello\" " + + "starts at <1> and ends at <4> in group 0\n" + + " got: \"Hello\""); + assertThat("Hello", match("hello", new RegexProperties(CASE_INSENSITIVE)) + .startsAt(1).endsAt(4)); + } + + @Test + public void matchCaseInsensitiveStartEnd() { + assertThat("Hello", match("hello", new RegexProperties(CASE_INSENSITIVE)) + .startsAt(0).endsAt(5)); + } + + @Test + public void matchStartEndMatcher$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: match \"(?ix) hello \" " + + "on (region from <0> to <4> or region from <1> to <5>)\n" + + " got: \"Hello\""); + assertThat("Hello", match(" hello ", new RegexProperties(CASE_INSENSITIVE, COMMENTS), anyOf(region(0, 4), region(1, 5)))); + } + + @Test + public void matchStartEndMatcher() { + assertThat("Hello", match(" hello ", new RegexProperties(CASE_INSENSITIVE, COMMENTS), anyOf(region(0, 5), region(1, 6)))); + } + + @Test + public void matchRegionMatcher$1$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: match \"(?i)hello\" " + + "on (region from <0> to <4> or region from <1> to <5>)\n" + + " got: \"Hello\""); + assertThat("Hello", match("hello", new RegexProperties(CASE_INSENSITIVE), anyOf(region(0, 4), region(1, 5)))); + } + + @Test + public void matchRegionMatcher$1() { + assertThat("Hello", match("hello", new RegexProperties(CASE_INSENSITIVE, COMMENTS), anyOf(region(0, 5), region(1, 6)))); + } + + @Test + public void matchRegionMatcher$2$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: match \"(?ix) hello \" " + + "on (region from <0> to <4> or region from <1> to <5>)\n" + + " got: \"Hello\""); + assertThat("Hello", match(" hello ", new RegexProperties(CASE_INSENSITIVE, COMMENTS), anyOf(region(0, 4), region(1, 5)))); + } + + @Test + public void matchRegionMatcher$2() { + assertThat("Hello", match(" hello ", new RegexProperties(CASE_INSENSITIVE, COMMENTS), anyOf(region(0, 5), region(1, 6)))); + } + + @Test + public void twoStartsWithMatch() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("not applicable when result matcher already specified"); + assertThat("", is(match("").startsAt(0).endsAt(0).startsAt(0))); + } + + @Test + public void twoEndsWithMatch() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("not applicable when result matcher already specified"); + assertThat("", is(match("").endsAt(0).startsAt(0).endsAt(0))); + } + + @Test + public void example1() { + RegexProperties regexProperties = new RegexProperties(CASE_INSENSITIVE).setStartRegion(1).setEndRegion(6); + assertThat("aAaBbcD", is(like("a*b", regexProperties))); + } + + @Test + public void example2() { + assertThat("Hi There!", is(like("hi|hello", new RegexProperties(CASE_INSENSITIVE)).startsAt(equalTo(0)))); + } + + @Test + public void example3() { + assertThat("Hi There!", is(like("Hi|Hello", region(0, 2)))); + } +} diff --git a/matchers/src/test/java/org/junit/contrib/matchers/TestIsThrowable.java b/matchers/src/test/java/org/junit/contrib/matchers/TestIsThrowable.java new file mode 100644 index 0000000..d52e797 --- /dev/null +++ b/matchers/src/test/java/org/junit/contrib/matchers/TestIsThrowable.java @@ -0,0 +1,157 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.io.IOException; +import java.io.NotSerializableException; +import java.io.ObjectStreamException; + +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.AnyOf.anyOf; +import static org.junit.Assert.assertThat; +import static org.junit.contrib.matchers.IsAssignableTo.assignableTo; +import static org.junit.contrib.matchers.IsThrowable.*; + +// junit & hamcrest matchers + +/** + * The purpose of this test is to test a functionality of + * {@link IsThrowable} matcher and to explain the use. + *

+ * @author Tibor17 + * @version 0.1 + * @see IsThrowable + * @since 0.1, Dec 11, 2011, 7:07:32 PM + */ +public final class TestIsThrowable { + @Rule + public final ExpectedException expectedExceptionRule = ExpectedException.none(); + + @Test + public void isType$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is type \n" + + " got: "); + + assertThat(new ArrayIndexOutOfBoundsException(), is(type(equalTo(NullPointerException.class)))); + } + + @Test + public void isType() { + assertThat(new ArrayIndexOutOfBoundsException(), is(type(equalTo(ArrayIndexOutOfBoundsException.class)))); + } + + @Test + public void isAssignableType$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is type assignable to java.lang.ArrayIndexOutOfBoundsException\n" + + " got: "); + + assertThat(new IndexOutOfBoundsException(), is(type(assignableTo(ArrayIndexOutOfBoundsException.class)))); + } + + @Test + public void isAssignableType() { + assertThat(new ArrayIndexOutOfBoundsException(), is(type(assignableTo(IndexOutOfBoundsException.class)))); + } + + @Test + public void isCause$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: cause \n" + + " got: "); + + final ObjectStreamException actualCause = new NotSerializableException(); + final ObjectStreamException fake = new NotSerializableException(); + assertThat(new IOException(actualCause), cause(equalTo(fake))); + } + + @Test + public void isCause() { + final NotSerializableException actualCause = new NotSerializableException(); + final ObjectStreamException expectedCause = actualCause; + assertThat(new IOException(actualCause), cause(is(expectedCause))); + } + + @Test + public void isMessage$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: message is \"any message\"\n" + + " got: "); + + Exception actual = new TestException("a test message", "a test localized message"); + + assertThat(actual, message(is("any message"))); + } + + @Test + public void isMessage() { + Exception actual = new TestException("a test message", "a test localized message"); + assertThat(actual, message(is("a test message"))); + } + + @Test + public void isLocalizedMessage$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: localized message is \"any localized message\"\n" + + " got: "); + + Exception actual = new TestException("a test message", "a test localized message"); + + assertThat(actual, localizedMessage(is("any localized message"))); + } + + @Test + public void isLocalizedMessage() { + Exception actual = new TestException("a test message", "a test localized message"); + assertThat(actual, localizedMessage(is("a test localized message"))); + } + + @Test + public void none$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("none"); + Exception actual = new Exception(); + assertThat(actual, is(none())); + } + + @Test + public void noNe() { + assertThat(null, is(none())); + } + + @Test + public void complement$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is complement of (type assignable to java.lang.Error or <>)\n" + + " got: "); + + assertThat(new IllegalAccessError(), + is(complementOf(anyOf(type(assignableTo(Error.class)), + type(assignableTo(RuntimeException.class)))))); + } + + @Test + public void complement() { + assertThat(new IOException(), + is(complementOf(anyOf(type(assignableTo(Error.class)), + type(assignableTo(RuntimeException.class)))))); + } +} diff --git a/matchers/src/test/java/org/junit/contrib/matchers/TestIsThrowing.java b/matchers/src/test/java/org/junit/contrib/matchers/TestIsThrowing.java new file mode 100644 index 0000000..67cb755 --- /dev/null +++ b/matchers/src/test/java/org/junit/contrib/matchers/TestIsThrowing.java @@ -0,0 +1,619 @@ +/* + * The copyright holders of this work license this file to You under + * the Apache License, Version 2.0 (the "License"); you may not use this + * file except in compliance with the License. You may obtain a copy of + * the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.junit.contrib.matchers; + +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.io.IOException; +import java.util.*; + +import static org.hamcrest.core.AllOf.allOf; +import static org.hamcrest.core.AnyOf.anyOf; +import static org.hamcrest.core.DescribedAs.describedAs; +import static org.hamcrest.core.Is.is; +import static org.hamcrest.core.IsEqual.equalTo; +import static org.hamcrest.core.IsNot.not; +import static org.junit.Assert.assertThat; +import static org.junit.contrib.matchers.IsAssignableTo.assignableTo; +import static org.junit.contrib.matchers.IsRegex.like; +import static org.junit.contrib.matchers.IsRegex.match; +import static org.junit.contrib.matchers.IsThrowable.localizedMessage; +import static org.junit.contrib.matchers.IsThrowable.type; +import static org.junit.contrib.matchers.IsThrowing.*; +import static org.junit.matchers.JUnitMatchers.both; +import static org.junit.matchers.JUnitMatchers.containsString; + +// junit & hamcrest matchers + +/** + * The purpose of this test is to test a functionality of + * {@link IsThrowing} matcher and to explain the use. + *

+ * @author Tibor17 + * @version 0.1 + * @see IsThrowing + * @since 0.1, Oct 3, 2011, 7:07:57 PM + */ +public final class TestIsThrowing { + @Rule + public final ExpectedException expectedExceptionRule = ExpectedException.none(); + + @Test + public void throwing$1() { + final Integer[] array = new Integer[2]; + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + int i = 0; + array[i++] = i; + array[i++] = i; + array[i++] = i; + } + }; + assertThat(block, is(throwing(ArrayIndexOutOfBoundsException.class))); + array[1] = array[0]; + } + + @Test + public void throwing$2() { + final Integer[] array = new Integer[2]; + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + int i = 0; + array[i++] = i; + array[i++] = i; + array[i++] = i; + } + }; + assertThat(block, is(throwing(IndexOutOfBoundsException.class))); + array[1] = array[0]; + } + + @Test + public void throwing$3() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("is not to throw any exception"); + final Integer[] array = new Integer[2]; + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + int i = 0; + array[i++] = i; + array[i++] = i; + array[i++] = i; + } + }; + assertThat(block, is(not(throwing()))); + array[1] = array[0]; + } + + @Test + public void throwing$3$1() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage(both(containsString("Expected: is not to throw any exception\r\n" + + "java.lang.ArrayIndexOutOfBoundsException: 2\r\n" + + "org.junit.contrib.matchers.TestIsThrowing$")) + .and(containsString("got: "))); + final Integer[] array = new Integer[2]; + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + int i = 0; + array[i++] = i; + array[i++] = i; + array[i++] = i; + } + }; + assertThat(block, is(not(throwingDeeply()))); + array[1] = array[0]; + } + + @Test + public void throwing$3$2() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage(both(containsString("Expected: is not to throw exceptions at all\r\n" + + "java.lang.ArrayIndexOutOfBoundsException: 2\r\n" + + "org.junit.contrib.matchers.TestIsThrowing$")) + .and(containsString("got: "))); + final Integer[] array = new Integer[2]; + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + int i = 0; + array[i++] = i; + array[i++] = i; + array[i++] = i; + } + }; + assertThat(block, is(notThrowingDeeply())); + array[1] = array[0]; + } + + @Test + public void throwing$4() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("is not to throw exceptions at all"); + final Integer[] array = new Integer[2]; + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + int i = 0; + array[i++] = i; + array[i++] = i; + array[i++] = i; + } + }; + assertThat(block, is(notThrowing())); + array[1] = array[0]; + } + + @Test + public void throwing$5$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage(both(containsString("Expected: is to throw exception(s) [java.lang.IndexOutOfBoundsException]\r\n")) + .and(containsString("org.junit.contrib.matchers.TestIsThrowing$")) + .and(containsString(" got: "))); + final Integer[] array = new Integer[2]; + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + Object[] copy = array; + copy[0] = "wrong type to assign"; + } + }; + assertThat(block, is(throwingDeeply(IndexOutOfBoundsException.class))); + array[0] = array[1]; + } + + @Test + public void throwing$5() { + final Integer[] array = new Integer[2]; + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + Object[] copy = array; + copy[0] = "wrong type to assign"; + } + }; + assertThat(block, is(not(throwingDeeply(IndexOutOfBoundsException.class)))); + array[0] = array[1]; + } + + @Test + public void throwing$6() { + final Integer[] array = new Integer[2]; + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + Object[] copy = array; + copy[0] = "wrong type to assign"; + } + }; + assertThat(block, is(notThrowingDeeply(IndexOutOfBoundsException.class))); + array[0] = array[1]; + } + + @Test + public void throwing$7() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("is not to throw exception(s) " + + "[java.lang.IndexOutOfBoundsException, java.lang.ArrayStoreException]\n" + + " got: "); + + final Integer[] array = new Integer[2]; + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + Object[] copy = array; + copy[0] = "wrong type to assign"; + } + }; + + assertThat(block, is(not(throwing(IndexOutOfBoundsException.class, ArrayStoreException.class)))); + + array[0] = array[1]; + } + + @Test + public void throwing$8() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage(both(containsString("Expected: is not to throw exception(s) " + + "[java.lang.IndexOutOfBoundsException, java.lang.ArrayStoreException]\r\n")) + .and(containsString(" got: "))); + + final Integer[] array = new Integer[2]; + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + Object[] copy = array; + copy[0] = "wrong type to assign"; + } + }; + + assertThat(block, is(not(throwingDeeply(IndexOutOfBoundsException.class, ArrayStoreException.class)))); + + array[0] = array[1]; + } + + @Test + public void throwing$9() { + final Integer[] array = new Integer[2]; + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + Object[] copy = array; + copy[0] = "wrong type to assign"; + } + }; + assertThat(block, is(throwing(IndexOutOfBoundsException.class, ArrayStoreException.class))); + array[0] = array[1]; + } + + @Test + public void throwing$10() { + final Integer[] array = new Integer[2]; + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + Object[] copy = array; + copy[0] = "wrong type to assign"; + } + }; + assertThat(block, is(throwingDeeply(IndexOutOfBoundsException.class, ArrayStoreException.class))); + array[0] = array[1]; + } + + @Test + public void throwingDescribedAs() { + final Integer[] array = new Integer[2]; + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + Object[] copy = array; + copy[new Random().nextInt(4)] = "wrong type to assign"; + } + }; + @SuppressWarnings("unchecked") + Class[] expected = new Class[] {IndexOutOfBoundsException.class, ArrayStoreException.class}; + assertThat(block, describedAs("\n%0 might be thrown due to the original array length is " + array.length + "," + + "\nor %1 must be thrown because only " + array.getClass().getComponentType().getName() + " is accepted\n", + is(throwingDeeply(expected)), + expected)); + array[0] = array[1]; + } + + @Test + @SuppressWarnings("unchecked") + public void throwingMutualBlock$0() { + expectedExceptionRule.expect(ClassCastException.class); + List list = Collections.checkedList(Arrays.asList("b", "c"), String.class); + final ListIterator it = list.listIterator(); + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + it.next(); + it.next(); + it.set("a"); + } + }; + assertThat(block, both(is(throwingDeeply(NoSuchElementException.class))).and(is(notThrowing()))); + list.set(0, new Object()); + } + + @Test + @SuppressWarnings("unchecked") + public void throwingMutualBlock$1() { + expectedExceptionRule.expect(ClassCastException.class); + List list = Collections.checkedList(Arrays.asList("b", "c"), String.class); + final ListIterator it = list.listIterator(); + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + it.next(); + it.next(); + it.set("a"); + } + }; + assertThat(block, allOf(is(notThrowing()), is(throwingDeeply(NoSuchElementException.class)))); + list.set(0, new Object()); + } + + @Test + public void thrownOkMessageOk$0() { + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() throws Exception { + throw new Exception(" a ny thin g "); + } + }; + assertThat(block, is(throwingDeeply().andMessage(like("a ny|thing")))); + } + + @Test + public void thrownOkMessageWrong$0() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage(both(containsString("Expected: " + + "is to throw any exception and message is like \"any|thing\"\r\n")) + .and(containsString("org.junit.contrib.matchers.TestIsThrowing$")) + .and(containsString(" got: "))); + + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() throws Exception { + throw new Exception(" an y thin g "); + } + }; + + assertThat(block, is(throwingDeeply().andMessage(like("any|thing")))); + } + + @Test + public void thrownOkNullMessageWrong$0() { + final AbstractCallable block = new Block() { + @Override + protected void run() { + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() throws Exception { + throw new Exception((String) null); + } + }; + assertThat(block, is(throwingDeeply().andMessage(like("any|thing")))); + + } + }; + assertThat(block, throwing(java.lang.AssertionError.class) + .withMessage("observed exception which produced unacceptable NULL message")); + + } + + @Test + public void thrownOkMessageOk$1() { + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() throws Exception { + throw new Exception(" a ny thin g "); + } + }; + assertThat(block, is(throwingDeeply().andMessage(anyOf(containsString("any"), containsString("thin"), containsString("thing"))))); + } + + @Test + public void thrownOkMessageWrong$1() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage(both(containsString("Expected: " + + "is to throw any exception and message is " + + "(a string containing \"any\" or a string containing \"thing\")\r\n")) + .and(containsString("org.junit.contrib.matchers.TestIsThrowing$")) + .and(containsString(" got: "))); + + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() throws Exception { + throw new Exception(" an y thin g "); + } + }; + + assertThat(block, is(throwingDeeply().andMessage(anyOf(containsString("any"), containsString("thing"))))); + } + + @Test + public void thrownOkNullMessageWrong$1() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("is to throw any exception " + + "and message " + + "is (a string containing \"any\" or a string containing \"thing\")"); + + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() throws Exception { + throw new Exception((String) null); + } + }; + + assertThat(block, is(throwingDeeply().andMessage(anyOf(containsString("any"), containsString("thing"))))); + } + + @Test + public void matchException$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is throwing type assignable to java.io.IOException\n" + + " got: "); + + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + throw new RuntimeException(); + } + }; + assertThat(block, is(throwing(type(assignableTo(IOException.class))))); + } + + @Test + public void matchException() { + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + throw new NullPointerException(); + } + }; + assertThat(block, is(throwing(type(assignableTo(RuntimeException.class))))); + } + + @Test + public void matchExceptionWithMessage$NegativeTest$1() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is throwing (localized message \"non-null value\" and (<> or <>))\n" + + " got: "); + + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + throw new NullPointerException("null value"); + } + }; + + assertThat(block, is(throwing( + allOf(localizedMessage(equalTo("non-null value")), + anyOf(type(assignableTo(IllegalArgumentException.class)), type(assignableTo(NullPointerException.class))))))); + } + + @Test + public void matchExceptionWithMessage$NegativeTest$2() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is throwing ((type assignable to java.lang.IllegalArgumentException " + + "or type assignable to java.lang.NullPointerException) " + + "and localized message \"non-null value\")\n" + + " got: "); + + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + throw new NullPointerException("null value"); + } + }; + + assertThat(block, is(throwing( + both(localizedMessage(equalTo("non-null value"))) + .and(anyOf(type(assignableTo(IllegalArgumentException.class)), type(assignableTo(NullPointerException.class))))))); + } + + @Test + public void matchExceptionWithMessage$NegativeTest$3() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is throwing ((type assignable to java.lang.IllegalArgumentException " + + "or type assignable to java.lang.NullPointerException) and localized message like \"non-null value\")\n" + + " got: "); + + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + throw new NullPointerException("null value"); + } + }; + + assertThat(block, is(throwing( + both(localizedMessage(like("non-null value"))) + .and(anyOf(type(assignableTo(IllegalArgumentException.class)), type(assignableTo(NullPointerException.class))))))); + } + + @Test + public void matchExceptionWithMessage$NegativeTest$4() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is throwing ((type assignable to java.lang.IllegalArgumentException " + + "or type assignable to java.lang.NullPointerException) " + + "and localized message match \"non-null value\")\n" + + " got: "); + + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + throw new NullPointerException("null value"); + } + }; + + assertThat(block, is(throwing( + both(localizedMessage(match("non-null value"))) + .and(anyOf(type(assignableTo(IllegalArgumentException.class)), type(assignableTo(NullPointerException.class))))))); + } + + @Test + public void matchExceptionWithMessage$1() { + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + throw new NullPointerException("null value"); + } + }; + + assertThat(block, is(throwing( + allOf(localizedMessage(is("null value")), + anyOf(type(assignableTo(IllegalArgumentException.class)), type(assignableTo(NullPointerException.class))))))); + } + + @Test + public void matchExceptionWithMessage$2() { + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + throw new NullPointerException("null value"); + } + }; + assertThat(block, is(throwing( + both(localizedMessage(is("null value"))) + .and(anyOf(type(assignableTo(IllegalArgumentException.class)), type(assignableTo(NullPointerException.class))))))); + } + + @Test + public void matchExceptionWithMessageRegex$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is to throw exception(s) [java.lang.RuntimeException] " + + "and message is match \"null(.*?)parameter\"\n" + + " got: "); + + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + throw new NullPointerException("must not be null value in method parameter"); + } + }; + + assertThat(block, is(throwing(RuntimeException.class) + .andMessage(IsRegex.match("null(.*?)parameter")))); + } + + @Test + public void matchExceptionWithMessageRegex() { + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + throw new NullPointerException("must not be null value in method parameter"); + } + }; + assertThat(block, is(throwing(RuntimeException.class) + .andMessage(IsRegex.match("(.*?)null(.*?)parameter")))); + } + + @Test + public void likeExceptionWithMessageRegex$NegativeTest() { + expectedExceptionRule.expect(AssertionError.class); + expectedExceptionRule.expectMessage("Expected: is to throw exception(s) [java.lang.RuntimeException] " + + "and message is like \"null(.*?aaa*)parameter\"\n" + + " got: "); + + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + throw new NullPointerException("must not be null value in method parameter"); + } + }; + assertThat(block, is(throwing(RuntimeException.class).andMessage(like("null(.*?aaa*)parameter")))); + } + + @Test + public void likeExceptionWithMessageRegex() { + @SuppressWarnings({"unchecked"}) + final AbstractCallable block = new Block() { + @Override protected void run() { + throw new NullPointerException("must not be null value in method parameter"); + } + }; + assertThat(block, is(throwing(RuntimeException.class) + .andMessage(like("null(.*?)parameter")))); + } +} \ No newline at end of file