/*
* Copyright (c) 2010-2016 Evolveum
*
* 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.
*/
package com.evolveum.midpoint.test.util;
import com.evolveum.midpoint.prism.Containerable;
import com.evolveum.midpoint.prism.PrismContainer;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismObject;
import com.evolveum.midpoint.prism.PrismPropertyValue;
import com.evolveum.midpoint.schema.constants.SchemaConstants;
import com.evolveum.midpoint.schema.processor.ResourceAttribute;
import com.evolveum.midpoint.schema.processor.ResourceAttributeDefinition;
import com.evolveum.midpoint.schema.processor.ResourceAttributeDefinitionImpl;
import com.evolveum.midpoint.schema.result.OperationResult;
import com.evolveum.midpoint.schema.result.OperationResultStatus;
import com.evolveum.midpoint.util.JAXBUtil;
import com.evolveum.midpoint.util.MiscUtil;
import com.evolveum.midpoint.util.exception.ObjectAlreadyExistsException;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.evolveum.midpoint.xml.ns._public.common.common_3.MetadataType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ObjectType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultStatusType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.OperationResultType;
import com.evolveum.midpoint.xml.ns._public.common.common_3.ShadowType;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.xml.datatype.DatatypeConfigurationException;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import javax.xml.namespace.QName;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.testng.AssertJUnit;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertTrue;
import static org.testng.AssertJUnit.fail;
/**
* Unit test utilities.
*
* @author Radovan Semancik
*/
public class TestUtil {
public static final int MAX_EXCEPTION_MESSAGE_LENGTH = 500;
public static final String TEST_LOG_PREFIX = "=====[ ";
public static final String TEST_LOG_SUFFIX = " ]======================================";
public static final String TEST_OUT_PREFIX = "\n\n=====[ ";
public static final String TEST_OUT_SUFFIX = " ]======================================\n";
public static final String TEST_OUT_SECTION_PREFIX = "\n\n----- ";
public static final String TEST_OUT_SECTION_SUFFIX = " --------------------------------------\n";
public static final String TEST_LOG_SECTION_PREFIX = "----- ";
public static final String TEST_LOG_SECTION_SUFFIX = " --------------------------------------";
private static final Pattern JAVA_VERSION_PATTERN = Pattern.compile("1\\.(\\d+)\\.\\d+_\\d+");
public static boolean checkResults = true;
private static DatatypeFactory datatypeFactory = null;
private static final Trace LOGGER = TraceManager.getTrace(TestUtil.class);
public static <T> void assertPropertyValueSetEquals(Collection<PrismPropertyValue<T>> actual, T... expected) {
Set<T> set = new HashSet<T>();
for (PrismPropertyValue<T> value : actual) {
set.add(value.getValue());
}
assertSetEquals(set, expected);
}
public static <T> void assertSetEquals(Collection<T> actual, T... expected) {
assertSetEquals(null, actual, expected);
}
public static <T> void assertSetEquals(String message, Collection<T> actual, T... expected) {
Set<T> expectedSet = new HashSet<T>();
expectedSet.addAll(Arrays.asList(expected));
Set<T> actualSet = new HashSet<T>();
actualSet.addAll(actual);
if (message != null) {
assertEquals(message, expectedSet, actualSet);
} else {
assertEquals(expectedSet, actualSet);
}
}
public static <T> void assertSetEquals(String message, T[] actual, T[] expected) {
assertTrue(message+"expected "+Arrays.toString(expected)+", was "+Arrays.toString(actual),
MiscUtil.unorderedArrayEquals(actual, expected));
}
public static String getNodeOid(Node node) {
Node oidNode = null;
if ((null == node.getAttributes())
|| (null == (oidNode = node.getAttributes().getNamedItem(
SchemaConstants.C_OID_ATTRIBUTE.getLocalPart())))
|| (StringUtils.isEmpty(oidNode.getNodeValue()))) {
return null;
}
String oid = oidNode.getNodeValue();
return oid;
}
public static void setAttribute(PrismObject<ShadowType> account, QName attrName, QName typeName,
PrismContext prismContext, String value) throws SchemaException {
PrismContainer<Containerable> attributesContainer = account.findContainer(ShadowType.F_ATTRIBUTES);
ResourceAttributeDefinition attrDef = new ResourceAttributeDefinitionImpl(attrName, typeName, prismContext);
ResourceAttribute attribute = attrDef.instantiate();
attribute.setRealValue(value);
attributesContainer.add(attribute);
}
public static void assertElement(List<Object> elements, QName elementQName, String value) {
for (Object element: elements) {
QName thisElementQName = JAXBUtil.getElementQName(element);
if (elementQName.equals(thisElementQName)) {
if (element instanceof Element) {
String thisElementContent = ((Element)element).getTextContent();
if (value.equals(thisElementContent)) {
return;
} else {
AssertJUnit.fail("Wrong value for element with name "+elementQName+"; expected "+value+"; was "+thisElementContent);
}
} else {
throw new IllegalArgumentException("Unexpected type of element "+elementQName+": "+element.getClass());
}
}
}
AssertJUnit.fail("No element with name "+elementQName);
}
public static void assertExceptionSanity(ObjectAlreadyExistsException e) {
LOGGER.debug("Excpetion (expected)", e, e);
System.out.println("Excpetion (expected)");
System.out.println(ExceptionUtils.getFullStackTrace(e));
assert !e.getMessage().isEmpty() : "Empty exception message";
assert e.getMessage().length() < MAX_EXCEPTION_MESSAGE_LENGTH : "Exception message too long ("
+e.getMessage().length()+" characters): "+e.getMessage();
}
public static void displayTestTile(String testName) {
System.out.println(TEST_OUT_PREFIX + testName + TEST_OUT_SUFFIX);
LOGGER.info(TEST_LOG_PREFIX + testName + TEST_LOG_SUFFIX);
}
public static void displayTestTile(Object testCase, String testName) {
System.out.println(TEST_OUT_PREFIX + testCase.getClass().getSimpleName() + "." + testName + TEST_OUT_SUFFIX);
LOGGER.info(TEST_LOG_PREFIX + testCase.getClass().getSimpleName() + "." + testName + TEST_LOG_SUFFIX);
}
public static void displayWhen(String testName) {
System.out.println(TEST_OUT_SECTION_PREFIX + " WHEN " + testName + TEST_OUT_SECTION_SUFFIX);
LOGGER.info(TEST_LOG_SECTION_PREFIX + " WHEN " + testName + TEST_LOG_SECTION_SUFFIX);
}
public static void displayThen(String testName) {
System.out.println(TEST_OUT_SECTION_PREFIX + " THEN " + testName + TEST_OUT_SECTION_SUFFIX);
LOGGER.info(TEST_LOG_SECTION_PREFIX + " THEN " + testName + TEST_LOG_SECTION_SUFFIX);
}
public static void info(String message) {
System.out.println(TEST_OUT_SECTION_PREFIX + message + TEST_OUT_SECTION_SUFFIX);
LOGGER.info(TEST_LOG_SECTION_PREFIX + message + TEST_LOG_SECTION_SUFFIX);
}
public static void assertSuccess(String message, OperationResult result, OperationResult originalResult, int stopLevel, int currentLevel, boolean warningOk) {
if (!checkResults) {
return;
}
if (result.getStatus() == null || result.getStatus().equals(OperationResultStatus.UNKNOWN)) {
String logmsg = message + ": undefined status ("+result.getStatus()+") on operation "+result.getOperation();
LOGGER.error(logmsg);
LOGGER.trace(logmsg + "\n" + originalResult.debugDump());
System.out.println(logmsg + "\n" + originalResult.debugDump());
fail(logmsg);
}
if (result.isHandledError()) {
// There may be errors deeper in this result, even fatal errors. that's ok, we can ignore them.
return;
} else if (result.isSuccess() || result.isNotApplicable()) {
// OK ... expected error is as good as success
} else if (warningOk && result.getStatus() == OperationResultStatus.WARNING) {
// OK
} else {
String logmsg = message + ": " + result.getStatus() + ": " + result.getMessage();
LOGGER.error(logmsg);
LOGGER.trace(logmsg + "\n" + originalResult.debugDump());
System.out.println(logmsg + "\n" + originalResult.debugDump());
assert false : logmsg;
}
if (stopLevel == currentLevel) {
return;
}
List<OperationResult> partialResults = result.getSubresults();
for (OperationResult subResult : partialResults) {
assertSuccess(message, subResult, originalResult, stopLevel, currentLevel + 1, warningOk);
}
}
/**
* level=-1 - check all levels
* level=0 - check only the top-level
* level=1 - check one level below top-level
* ...
*
* @param message
* @param result
* @param level
*/
public static void assertSuccess(String message, OperationResult result, int level) {
assertSuccess(message, result, result, level, 0, false);
}
public static void assertSuccess(String message, OperationResult result) {
assertSuccess(message, result,-1);
}
public static void assertSuccess(OperationResultType result) {
assertSuccess(result.getOperation(), result);
}
public static void assertSuccess(String message, OperationResultType result) {
if (!checkResults) {
return;
}
assertNotNull(message + ": null result", result);
// Ignore top-level if the operation name is not set
if (result.getOperation()!=null) {
if (result.getStatus() == null || result.getStatus() == OperationResultStatusType.UNKNOWN) {
fail(message + ": undefined status ("+result.getStatus()+") on operation "+result.getOperation());
}
if (result.getStatus() != OperationResultStatusType.SUCCESS
&& result.getStatus() != OperationResultStatusType.NOT_APPLICABLE
&& result.getStatus() != OperationResultStatusType.HANDLED_ERROR) {
fail(message + ": " + result.getMessage() + " ("+result.getStatus()+")");
}
}
List<OperationResultType> partialResults = result.getPartialResults();
for (OperationResultType subResult : partialResults) {
if (subResult==null) {
fail(message+": null subresult under operation "+result.getOperation());
}
if (subResult.getOperation()==null) {
fail(message+": null subresult operation under operation "+result.getOperation());
}
assertSuccess(message, subResult);
}
}
public static void assertInProgressOrSuccess(OperationResult result) {
if (!result.isInProgress()) {
assertSuccess("Operation "+result.getOperation()+" result", result);
}
}
public static void assertSuccess(OperationResult result) {
assertSuccess("Operation "+result.getOperation()+" result", result);
}
public static void assertSuccess(OperationResult result, int depth) {
assertSuccess("Operation "+result.getOperation()+" result", result, depth);
}
public static void assertStatus(OperationResult result, OperationResultStatus expectedStatus) {
assertEquals("Operation "+result.getOperation()+" result", expectedStatus, result.getStatus());
}
public static void assertStatus(OperationResultType result, OperationResultStatusType expectedStatus) {
assertEquals("Operation "+result.getOperation()+" result", expectedStatus, result.getStatus());
}
public static boolean hasWarningAssertSuccess(String message, OperationResultType result) {
boolean hasWarning = false;
// Ignore top-level if the operation name is not set
if (result.getOperation()!=null) {
if (result.getStatus() == OperationResultStatusType.WARNING) {
// Do not descent into warnings. There may be lions inside. Or errors.
return true;
} else {
if (result.getStatus() == null || result.getStatus() == OperationResultStatusType.UNKNOWN) {
fail(message + ": undefined status ("+result.getStatus()+") on operation "+result.getOperation());
}
if (result.getStatus() != OperationResultStatusType.SUCCESS
&& result.getStatus() != OperationResultStatusType.NOT_APPLICABLE
&& result.getStatus() != OperationResultStatusType.HANDLED_ERROR) {
fail(message + ": " + result.getMessage() + " ("+result.getStatus()+")");
}
}
}
List<OperationResultType> partialResults = result.getPartialResults();
for (OperationResultType subResult : partialResults) {
if (subResult==null) {
fail(message+": null subresult under operation "+result.getOperation());
}
if (subResult.getOperation()==null) {
fail(message+": null subresult operation under operation "+result.getOperation());
}
if (hasWarningAssertSuccess(message, subResult)) {
hasWarning = true;
}
}
return hasWarning;
}
public static void assertWarning(String message, OperationResultType result) {
if (!checkResults) {
return;
}
assert hasWarningAssertSuccess(message, result) : message + ": does not have warning";
}
public static void assertFailure(String message, OperationResult result) {
assertTrue(message, result.isError());
assertNoUnknown(result);
}
public static void assertFailure(OperationResult result) {
if (!result.isError()) {
String message = "Expected that operation "+result.getOperation()+" fails, but the result was "+result.getStatus();
System.out.println(message);
System.out.println(result.debugDump());
LOGGER.error("{}",message);
LOGGER.error("{}",result.debugDump());
AssertJUnit.fail(message);
}
assertNoUnknown(result);
}
public static void assertPartialError(OperationResult result) {
assertTrue("Expected that operation "+result.getOperation()+" fails partially, but the result was "+result.getStatus(), result.getStatus() == OperationResultStatus.PARTIAL_ERROR);
assertNoUnknown(result);
}
public static void assertResultStatus(OperationResult result, OperationResultStatus expectedStatus) {
assertTrue("Expected that operation "+result.getOperation()+" will result with "+expectedStatus+", but the result was "+result.getStatus(), result.getStatus() == expectedStatus);
assertNoUnknown(result);
}
public static void assertFailure(OperationResultType result) {
assertFailure(null, result);
}
public static void assertFailure(String message, OperationResultType result) {
assertTrue((message == null ? "" : message + ": ") +
"Expected that operation "+result.getOperation()+" fails, but the result was "+result.getStatus(),
OperationResultStatusType.FATAL_ERROR == result.getStatus() ||
OperationResultStatusType.PARTIAL_ERROR == result.getStatus()) ;
assertNoUnknown(result);
}
public static void assertNoUnknown(OperationResult result) {
if (result.isUnknown()) {
AssertJUnit.fail("Unkwnown status for operation "+result.getOperation());
}
for (OperationResult subresult: result.getSubresults()) {
assertNoUnknown(subresult);
}
}
public static void assertNoUnknown(OperationResultType result) {
if (result.getStatus() == OperationResultStatusType.UNKNOWN) {
AssertJUnit.fail("Unkwnown status for operation "+result.getOperation());
}
for (OperationResultType subresult: result.getPartialResults()) {
assertNoUnknown(subresult);
}
}
public static void assertSuccessOrWarning(String message, OperationResult result, int level) {
assertSuccess(message, result, result, level, 0, true);
}
public static void assertSuccessOrWarning(String message, OperationResult result) {
assertSuccess(message, result, result, -1, 0, true);
}
public static void assertWarning(String message, OperationResult result) {
assertWarning(message, result, -1, 0);
}
public static boolean hasWarningAssertSuccess(String message, OperationResult result, OperationResult originalResult, int stopLevel, int currentLevel) {
if (result.getStatus() == null || result.getStatus().equals(OperationResultStatus.UNKNOWN)) {
String logmsg = message + ": undefined status ("+result.getStatus()+") on operation "+result.getOperation();
LOGGER.error(logmsg);
LOGGER.trace(logmsg + "\n" + originalResult.debugDump());
System.out.println(logmsg + "\n" + originalResult.debugDump());
fail(logmsg);
}
if (result.isWarning()) {
// Do not descent into warnings. There may be lions inside. Or errors.
return true;
}
if (result.isSuccess() || result.isHandledError() || result.isNotApplicable()) {
// OK ... expected error is as good as success
} else {
String logmsg = message + ": " + result.getStatus() + ": " + result.getMessage();
LOGGER.error(logmsg);
LOGGER.trace(logmsg + "\n" + originalResult.debugDump());
System.out.println(logmsg + "\n" + originalResult.debugDump());
assert false : logmsg;
}
if (stopLevel == currentLevel) {
return false;
}
boolean hasWarning = false;
List<OperationResult> partialResults = result.getSubresults();
for (OperationResult subResult : partialResults) {
if (hasWarningAssertSuccess(message, subResult, originalResult, stopLevel, currentLevel + 1)) {
hasWarning = true;
}
}
return hasWarning;
}
public static void assertWarning(String message, OperationResult result, int stopLevel, int currentLevel) {
if (!checkResults) {
return;
}
hasWarningAssertSuccess(message, result, result, -1, 0);
}
public static void assertInProgress(String message, OperationResult result) {
assertTrue("Expected result IN_PROGRESS but it was "+result.getStatus()+" in "+message,
result.getStatus() == OperationResultStatus.IN_PROGRESS);
}
public static String getErrorMessage(OperationResult result) {
if (result.isError()) {
return result.getMessage();
}
for (OperationResult subresult: result.getSubresults()) {
String message = getErrorMessage(subresult);
if (message != null) {
return message;
}
}
return null;
}
public static List<OperationResult> selectSubresults(OperationResult result, String... operationNames) {
List<OperationResult> retval = new ArrayList<>();
selectSubresultsInternal(retval, result, operationNames);
return retval;
}
private static void selectSubresultsInternal(List<OperationResult> retval, OperationResult result, String... operationNames) {
if (result == null) {
return; // should not occur actually
}
for (int i = 0; i < operationNames.length; i++) {
if (operationNames[i].equals(result.getOperation())) {
retval.add(result);
break;
}
}
for (OperationResult subresult : result.getSubresults()) {
selectSubresultsInternal(retval, subresult, operationNames);
}
}
public static String execSystemCommand(String command) throws IOException, InterruptedException {
return execSystemCommand(command, false);
}
public static String execSystemCommand(String command, boolean ignoreExitCode) throws IOException, InterruptedException {
Runtime runtime = Runtime.getRuntime();
LOGGER.debug("Executing system command: {}", command);
Process process = runtime.exec(command);
int exitCode = process.waitFor();
LOGGER.debug("Command exit code: {}", exitCode);
BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
StringBuilder output = new StringBuilder();
String line = null;
while ((line = reader.readLine()) != null) {
output.append(line);
}
reader.close();
String outstring = output.toString();
LOGGER.debug("Command output:\n{}",outstring);
if (!ignoreExitCode && exitCode != 0) {
String msg = "Execution of command '"+command+"' failed with exit code "+exitCode;
LOGGER.error("{}", msg);
throw new IOException(msg);
}
return outstring;
}
public static void assertBetween(String message, XMLGregorianCalendar start, XMLGregorianCalendar end,
XMLGregorianCalendar actual) {
assertNotNull(message + " is null", actual);
if (start != null) {
assertTrue(message+": expected time to be after "+start+" but it was "+actual,
actual.compare(start) == DatatypeConstants.GREATER || actual.compare(start) == DatatypeConstants.EQUAL);
}
if (end != null) {
assertTrue(message+": expected time to be before "+end+" but it was "+actual,
actual.compare(end) == DatatypeConstants.LESSER || actual.compare(end) == DatatypeConstants.EQUAL);
}
}
public static void assertBetween(String message, Long start, Long end,
Long actual) {
assertNotNull(message + " is null", actual);
if (start != null) {
assertTrue(message+": expected time to be after "+start+" but it was "+actual, actual >= start);
}
if (end != null) {
assertTrue(message+": expected time to be before "+end+" but it was "+actual, actual <= end);
}
}
public static void assertEqualsTimestamp(String message, XMLGregorianCalendar expected, XMLGregorianCalendar actual) {
assertNotNull(message+"; expected "+expected, actual);
assertTrue(message+"; expected "+expected+" but was "+actual, expected.compare(actual) == 0);
}
public static void assertCreateTimestamp(PrismObject<? extends ObjectType> object, XMLGregorianCalendar start,
XMLGregorianCalendar end) {
MetadataType metadata = object.asObjectable().getMetadata();
assertNotNull("No metadata in "+object, metadata);
assertBetween("createTimestamp in "+object, start, end, metadata.getCreateTimestamp());
}
public static void assertModifyTimestamp(PrismObject<? extends ObjectType> object, XMLGregorianCalendar start,
XMLGregorianCalendar end) {
assertModifyTimestamp(object, start, end, null);
}
public static void assertModifyTimestamp(PrismObject<? extends ObjectType> object, XMLGregorianCalendar start,
XMLGregorianCalendar end, String channel) {
MetadataType metadata = object.asObjectable().getMetadata();
assertNotNull("No metadata in "+object, metadata);
assertBetween("modifyTimestamp in "+object, start, end, metadata.getModifyTimestamp());
if (channel != null) {
assertEquals("Wrong channel", channel, metadata.getModifyChannel());
}
}
public static XMLGregorianCalendar currentTime() {
// This cannot use XmlTypeConverter as we want to use also in tests that do not depend on prism
GregorianCalendar gregorianCalendar = new GregorianCalendar();
gregorianCalendar.setTimeInMillis(System.currentTimeMillis());
return getDatatypeFactory().newXMLGregorianCalendar(gregorianCalendar);
}
private static DatatypeFactory getDatatypeFactory() {
if (datatypeFactory == null) {
try {
datatypeFactory = DatatypeFactory.newInstance();
} catch (DatatypeConfigurationException ex) {
throw new IllegalStateException("Cannot construct DatatypeFactory: " + ex.getMessage(), ex);
}
}
return datatypeFactory;
}
public static int getJavaMajorVersion() {
String javaVersionString = System.getProperty("java.version");
Matcher matcher = JAVA_VERSION_PATTERN.matcher(javaVersionString);
if (matcher.matches()) {
return Integer.parseInt(matcher.group(1));
} else {
throw new IllegalStateException("Cannot match java version string '"+javaVersionString+"'");
}
}
public static void assertMessageContains(String message, String expectedSubstring) {
assertTrue("Expected that message will contain substring '"+expectedSubstring+"', but it did not. Message: "+message,
message.contains(expectedSubstring));
}
// WARNING! Only works on Linux
public static int getPid() throws NumberFormatException, IOException {
return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
}
}