package picard.util;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import org.testng.Assert;
import static java.lang.Math.abs;
/**
* Small class implementing some utility functions that are useful for test and interfacing with the TestNG framework.
*/
public class TestNGUtil {
static final double EPSILON = 1e-300; //a constant near the smallest possible positive value representable in double,
// not actually the smallest possible value on purpose, since that would be indistinguishable from 0 and then useless.
// This is small enough to be meaningless, but representable.
public static abstract class SingleTestUnitTest<TestClazz extends TestNGParameterizable> {
@DataProvider(name = "testcases")
Object[][] getParams() {
return TestNGUtil.generateDataProvider(getTestcases());
}
@Test(dataProvider = "testcases")
public void doMetaTest(final TestClazz testcase) {
// Delegate to another method to avoid overwriting of annotations.
doTest(testcase);
}
abstract public Iterable<TestClazz> getTestcases();
abstract public void doTest(TestClazz testcase);
}
/**
* Interface for exposing an implementation for converting an object into a Object[] appropriate for positional
* parameter-passing as performed by TestNG. Useful for simplifying data sources.
*/
public static class TestNGParameterizable {
public Object[] toObjectArray() {
return new Object[]{this};
}
}
public static Object[][] generateDataProvider(final TestNGParameterizable[] testcases) {
return generateDataProvider(Arrays.asList(testcases));
}
public static Object[][] generateDataProvider(final Iterable<? extends TestNGParameterizable> testcases) {
final Iterator<? extends TestNGParameterizable> i = testcases.iterator();
final List<Object[]> parameterList = new LinkedList<Object[]>();
while (i.hasNext()) {
parameterList.add(i.next().toObjectArray());
}
Object[][] parameterArray = new Object[parameterList.size()][];
for (int j = 0; j < parameterList.size(); j++) {
parameterArray[j] = parameterList.get(j);
}
return parameterArray;
}
//TestNG doesn't have utility functions with these signatures
/**
* Small utility function for determining if two doubles are within a _relative_ accuracy of each other.
*
* @param lhs first number
* @param rhs second number
* @param accuracy maximal allowed relative difference between the two numbers
* @return true if numbers are within the relative tolerance of each other
*/
public static boolean compareDoubleWithAccuracy(final double lhs, final double rhs, final double accuracy) {
if (accuracy <= 0) throw new IllegalArgumentException("Accuracy must be positive.");
return abs(lhs - rhs) / (abs(lhs) + abs(rhs) + EPSILON) < accuracy;
}
public static void assertEqualDoubleArrays(final double[] lhs, final double[] rhs, final double accuracy) {
Assert.assertNotNull(lhs);
Assert.assertNotNull(rhs);
if (accuracy <= 0) throw new IllegalArgumentException("Accuracy must be positive.");
Assert.assertEquals(lhs.length, rhs.length, "Arrays not same length: " + lhs.length + " vs. " + rhs.length);
for (int i = 0; i < lhs.length; ++i) {
Assert.assertTrue(compareDoubleWithAccuracy(lhs[i], rhs[i], accuracy), "Arrays disagree at position " + i + ": " + lhs[i] + " vs. " + rhs[i] + ". ");
}
}
}