/*license*\
XBN-Java: Copyright (C) 2014, Jeff Epstein (aliteralmind __DASH__ github __AT__ yahoo __DOT__ com)
This software is dual-licensed under the:
- Lesser General Public License (LGPL) version 3.0 or, at your option, any later version;
- Apache Software License (ASL) version 2.0.
Either license may be applied at your discretion. More information may be found at
- http://en.wikipedia.org/wiki/Multi-licensing.
The text of both licenses is available in the root directory of this project, under the names "LICENSE_lgpl-3.0.txt" and "LICENSE_asl-2.0.txt". The latest copies may be downloaded at:
- LGPL 3.0: https://www.gnu.org/licenses/lgpl-3.0.txt
- ASL 2.0: http://www.apache.org/licenses/LICENSE-2.0.txt
\*license*/
package com.github.xbn.testdev;
import static org.junit.Assert.*;
import com.github.xbn.lang.CrashIfObject;
import com.github.xbn.lang.reflect.InvokeMethodWithRtx;
import static com.github.xbn.lang.XbnConstants.*;
/**
<p>Assert a Java application contains the expected console output.</p>
{@.codelet com.github.xbn.examples.testdev.UnitTestAppOutputContainsXmpl%eliminateCommentBlocksAndPackageDecl()}
* @since 0.1.0
* @author Copyright (C) 2014, Jeff Epstein ({@code aliteralmind __DASH__ github __AT__ yahoo __DOT__ com}), dual-licensed under the LGPL (version 3.0 or later) or the ASL (version 2.0). See source code for details. <a href="http://xbnjava.aliteralmind.com">{@code http://xbnjava.aliteralmind.com}</a>, <a href="https://github.com/aliteralmind/xbnjava">{@code https://github.com/aliteralmind/xbnjava}</a>
**/
public class VerifyApplicationOutput {
/**
* <p>Equal to {@code new String[]{}}</p>
*/
public static final String[] EMPTY_STRING_ARRAY = new String[]{};
/**
<p>Verify a Java application contains the expected console output, in the expected order, with no command line parameters.</p>
* @return <code>getBadIndexWithParams(String, Class, String[], String...) getBadIndexWithParams(forbiddenOutputs_ifNonNull, class_containingMainFunction, {@link #EMPTY_STRING_ARRAY}, xpctdOutputs_inOrder)</code>
*/
public static final int getBadIndexWithNoParams(String[] forbiddenOutputs_ifNonNull, Class<?> class_containingMainFunction, String... xpctdOutputs_inOrder) {
return getBadIndexWithParams(forbiddenOutputs_ifNonNull, class_containingMainFunction, EMPTY_STRING_ARRAY, xpctdOutputs_inOrder);
}
/**
<p>Verify a Java application contains the expected console output, in the expected order, with command line parameters.</p>
* @param forbiddenOutputs_ifNonNull If non-{@code null}, an array of strings that, if any item appears anywhere in the output, the test fails ({@link org.junit.Assert#assertTrue(String, boolean) assertTrue}{@code (<i>[helpful-error-message]</i>, false)} is called). May not contain null elements, and <i>should</i> not be empty or contain empty elements.
* @param class_containingMainFunction The class to execute. May not be {@code null}.
* @param cmd_lineParams The command line parameters passed to the <a href="http://docs.oracle.com/javase/tutorial/getStarted/application/index.html#MAIN">{@code main} function</a>. May not be {@code null}.
* @param xpctdOutputs_inOrder The array of strings that are required to be in the output, in order.
* @return <ul>
<li>{@code -1}: If all output is as expected.</li>
<li>{@code -2} or less: Indicates the index of the element in {@code forbiddenOutputs_ifNonNull} that was found. This is the index plus two, then multiplied by {@code -1}. For example, if element index three is found, then {@code ((3 + 2) * -1)=(5 * -1)=-5} is returned.</li>
<li>The index in {@code xpctdOutputs_inOrder}, of the first element not found.</li>
</ul>
*/
public static final int getBadIndexWithParams(String[] forbiddenOutputs_ifNonNull, Class<?> class_containingMainFunction, String[] cmd_lineParams, String... xpctdOutputs_inOrder) {
// CrashIfArray.nullEmpty(xpctdOutputs_inOrder, "xpctdOutputs_inOrder");
String output = InvokeMethodWithRtx.getApplicationOutput(class_containingMainFunction, cmd_lineParams, "Obtaining output for unit test");
int ix = 0;
try {
for(int i = 0; i < xpctdOutputs_inOrder.length; i++) {
String s = xpctdOutputs_inOrder[i];
try {
if(output.indexOf(s, ix) == -1) {
return i;
}
} catch(NullPointerException npx) {
throw CrashIfObject.nullOrReturnCause(s, "xpctdOutputs_inOrder[" + i + "]", null, npx);
}
ix += s.length();
}
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(xpctdOutputs_inOrder, "xpctdOutputs_inOrder", null, rx);
}
if(forbiddenOutputs_ifNonNull == null) {
return -1;
}
for(int i = 0; i < forbiddenOutputs_ifNonNull.length; i++) {
String forbidden = forbiddenOutputs_ifNonNull[i];
try {
if(output.indexOf(forbidden) != -1) {
return ((i + 2) * -1);
}
} catch(NullPointerException npx) {
throw CrashIfObject.nullOrReturnCause(forbidden, "forbiddenOutputs_ifNonNull[" + i + "]", null, npx);
}
}
return -1;
}
/**
<p>If the application does not have the expected output, <code>{@link org.junit.Assert#fail() fail}()</code>. Otherwise, {@link org.junit.Assert#assertTrue(boolean) assertTrue}{@code (true)}.</p>
* <p>Equal to
<br/> <code>{@link #assertWithParameters(DisplayOutputToConsole, String, Class, String[], String...) assertWithParameters}(out_toConsole.isYes(), forbiddenOutputs_ifNonNull, class_containingMainFunction, EMPTY_STRING_ARRAY, xpctdOutputs_inOrder)</code></p>
*/
public static final void assertWithNoParameters(DisplayOutputToConsole out_toConsole, String[] forbiddenOutputs_ifNonNull, Class<?> class_containingMainFunction, String... xpctdOutputs_inOrder) {
assertWithParameters(out_toConsole, forbiddenOutputs_ifNonNull, class_containingMainFunction, EMPTY_STRING_ARRAY, xpctdOutputs_inOrder);
}
/**
<p>If the application does not have the expected output, <code>{@link org.junit.Assert#fail() fail}()</code>. Otherwise, {@link org.junit.Assert#assertTrue(boolean) assertTrue}{@code (true)}.</p>
<p>This calls
<br/> <code>{@link #getBadIndexWithParams(String, Class, String[], String...) getBadIndexWithParams}(forbiddenOutputs_ifNonNull, class_containingMainFunction, cmd_lineParams, xpctdOutputs_inOrder)</code>
<br/>If it returns {@code -1}, this calls {@code assertTrue(true)}. Otherwise, this calls {@code fail()} with a diagnostic message.</p>
* @param out_toConsole If {@link com.github.xbn.testdev.DisplayOutputToConsole#YES YES}, output is also displayed on the console. If {@link com.github.xbn.testdev.DisplayOutputToConsole#NO NO}, it's suppressed. May not be {@code null}.
*/
public static final void assertWithParameters(DisplayOutputToConsole out_toConsole, String[] forbiddenOutputs_ifNonNull, Class<?> class_containingMainFunction, String[] cmd_lineParams, String... xpctdOutputs_inOrder) {
int badIdx = getBadIndexWithParams(forbiddenOutputs_ifNonNull, class_containingMainFunction, cmd_lineParams, xpctdOutputs_inOrder);
try {
if(out_toConsole.isYes()) {
System.out.println("Console output for class " + class_containingMainFunction.getName() + ": " + LINE_SEP + getOutputDisplay(class_containingMainFunction, cmd_lineParams));
}
} catch(RuntimeException rx) {
throw CrashIfObject.nullOrReturnCause(out_toConsole, "out_toConsole", null, rx);
}
if(badIdx == -1) {
assertTrue(true);
return;
}
if(badIdx <= -2) {
badIdx = (badIdx * -1) - 2;
fail(class_containingMainFunction.getName() + ".main(s[]): Forbidden item element " + badIdx + " found in output: \"" + forbiddenOutputs_ifNonNull[badIdx] + "\" -- All output:" + LINE_SEP + getOutputDisplay(class_containingMainFunction, cmd_lineParams));
return;
}
try {
fail(class_containingMainFunction.getName() + ".main(s[]): xpctdOutputs_inOrder[" + badIdx + "] not found in output (or not in its expected position). xpctdOutputs_inOrder[" + badIdx + "]: \"" + xpctdOutputs_inOrder[badIdx] + "\"" + LINE_SEP + getOutputDisplay(class_containingMainFunction, cmd_lineParams));
} catch(ArrayIndexOutOfBoundsException abx) {
throw new ArrayIndexOutOfBoundsException("getBadIndexWithParams(s,c,s...) returned " + badIdx + ": " + abx);
}
}
private static final String getOutputDisplay(Class<?> class_containingMainFunction, String[] cmd_lineParams) {
return "--APPLICATION OUTPUT start--" + LINE_SEP +
InvokeMethodWithRtx.getApplicationOutput(class_containingMainFunction, cmd_lineParams, "Obtaining output for unit test") +
"--APPLICATION OUTPUT end--";
}
}