aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests/MathTest.cpp
diff options
context:
space:
mode:
authorGravatar reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2009-02-27 16:24:51 +0000
committerGravatar reed@android.com <reed@android.com@2bbb7eff-a529-9590-31e7-b0007b416f81>2009-02-27 16:24:51 +0000
commited673310e2551e64d8196f7776d7d4c92085f8c2 (patch)
treeac5a737aaaf66c97be7109eb980beadd35e30e25 /tests/MathTest.cpp
parent3469c76c40790b409621fd7eff34f56240718549 (diff)
add initial unittest framework (tests)
move some previous unittests out of core classes and into tests git-svn-id: http://skia.googlecode.com/svn/trunk@96 2bbb7eff-a529-9590-31e7-b0007b416f81
Diffstat (limited to 'tests/MathTest.cpp')
-rw-r--r--tests/MathTest.cpp400
1 files changed, 400 insertions, 0 deletions
diff --git a/tests/MathTest.cpp b/tests/MathTest.cpp
new file mode 100644
index 0000000000..a759ab19a1
--- /dev/null
+++ b/tests/MathTest.cpp
@@ -0,0 +1,400 @@
+#include "Test.h"
+#include "SkPoint.h"
+#include "SkRandom.h"
+
+#if defined(SkLONGLONG)
+static int symmetric_fixmul(int a, int b) {
+ int sa = SkExtractSign(a);
+ int sb = SkExtractSign(b);
+
+ a = SkApplySign(a, sa);
+ b = SkApplySign(b, sb);
+
+#if 1
+ int c = (int)(((SkLONGLONG)a * b) >> 16);
+
+ return SkApplySign(c, sa ^ sb);
+#else
+ SkLONGLONG ab = (SkLONGLONG)a * b;
+ if (sa ^ sb) {
+ ab = -ab;
+ }
+ return ab >> 16;
+#endif
+}
+#endif
+
+static void check_length(skiatest::Reporter* reporter,
+ const SkPoint& p, SkScalar targetLen) {
+#ifdef SK_CAN_USE_FLOAT
+ float x = SkScalarToFloat(p.fX);
+ float y = SkScalarToFloat(p.fY);
+ float len = sk_float_sqrt(x*x + y*y);
+
+ len /= SkScalarToFloat(targetLen);
+
+ REPORTER_ASSERT(reporter, len > 0.999f && len < 1.001f);
+#endif
+}
+
+#if defined(SK_CAN_USE_FLOAT)
+
+static float nextFloat(SkRandom& rand) {
+ SkFloatIntUnion data;
+ data.fSignBitInt = rand.nextU();
+ return data.fFloat;
+}
+
+/* returns true if a == b as resulting from (int)x. Since it is undefined
+ what to do if the float exceeds 2^32-1, we check for that explicitly.
+ */
+static bool equal_float_native_skia(float x, uint32_t ni, uint32_t si) {
+ if (!(x == x)) { // NAN
+ return si == SK_MaxS32 || si == SK_MinS32;
+ }
+ // for out of range, C is undefined, but skia always should return NaN32
+ if (x > SK_MaxS32) {
+ return si == SK_MaxS32;
+ }
+ if (x < -SK_MaxS32) {
+ return si == SK_MinS32;
+ }
+ return si == ni;
+}
+
+static void assert_float_equal(skiatest::Reporter* reporter, const char op[],
+ float x, uint32_t ni, uint32_t si) {
+ if (!equal_float_native_skia(x, ni, si)) {
+ SkString desc;
+ desc.printf("%s float %g bits %x native %x skia %x\n", op, x, ni, si);
+ reporter->reportFailed(desc);
+ }
+}
+
+static void test_float_cast(skiatest::Reporter* reporter, float x) {
+ int ix = (int)x;
+ int iix = SkFloatToIntCast(x);
+ assert_float_equal(reporter, "cast", x, ix, iix);
+}
+
+static void test_float_floor(skiatest::Reporter* reporter, float x) {
+ int ix = (int)floor(x);
+ int iix = SkFloatToIntFloor(x);
+ assert_float_equal(reporter, "floor", x, ix, iix);
+}
+
+static void test_float_round(skiatest::Reporter* reporter, float x) {
+ double xx = x + 0.5; // need intermediate double to avoid temp loss
+ int ix = (int)floor(xx);
+ int iix = SkFloatToIntRound(x);
+ assert_float_equal(reporter, "round", x, ix, iix);
+}
+
+static void test_float_ceil(skiatest::Reporter* reporter, float x) {
+ int ix = (int)ceil(x);
+ int iix = SkFloatToIntCeil(x);
+ assert_float_equal(reporter, "ceil", x, ix, iix);
+}
+
+static void test_float_conversions(skiatest::Reporter* reporter, float x) {
+ test_float_cast(reporter, x);
+ test_float_floor(reporter, x);
+ test_float_round(reporter, x);
+ test_float_ceil(reporter, x);
+}
+
+static void test_int2float(skiatest::Reporter* reporter, int ival) {
+ float x0 = (float)ival;
+ float x1 = SkIntToFloatCast(ival);
+ float x2 = SkIntToFloatCast_NoOverflowCheck(ival);
+ REPORTER_ASSERT(reporter, x0 == x1);
+ REPORTER_ASSERT(reporter, x0 == x2);
+}
+
+static void unittest_fastfloat(skiatest::Reporter* reporter) {
+ SkRandom rand;
+ size_t i;
+
+ static const float gFloats[] = {
+ 0.f, 1.f, 0.5f, 0.499999f, 0.5000001f, 1.f/3,
+ 0.000000001f, 1000000000.f, // doesn't overflow
+ 0.0000000001f, 10000000000.f // does overflow
+ };
+ for (i = 0; i < SK_ARRAY_COUNT(gFloats); i++) {
+ // SkDebugf("---- test floats %g %d\n", gFloats[i], (int)gFloats[i]);
+ test_float_conversions(reporter, gFloats[i]);
+ test_float_conversions(reporter, -gFloats[i]);
+ }
+
+ for (int outer = 0; outer < 100; outer++) {
+ rand.setSeed(outer);
+ for (i = 0; i < 100000; i++) {
+ float x = nextFloat(rand);
+ test_float_conversions(reporter, x);
+ }
+
+ test_int2float(reporter, 0);
+ test_int2float(reporter, 1);
+ test_int2float(reporter, -1);
+ for (i = 0; i < 100000; i++) {
+ // for now only test ints that are 24bits or less, since we don't
+ // round (down) large ints the same as IEEE...
+ int ival = rand.nextU() & 0xFFFFFF;
+ test_int2float(reporter, ival);
+ test_int2float(reporter, -ival);
+ }
+ }
+}
+
+#endif
+
+static void test_muldiv255(skiatest::Reporter* reporter) {
+#ifdef SK_CAN_USE_FLOAT
+ for (int a = 0; a <= 255; a++) {
+ for (int b = 0; b <= 255; b++) {
+ int ab = a * b;
+ float s = ab / 255.0f;
+ int round = (int)floorf(s + 0.5f);
+ int trunc = (int)floorf(s);
+
+ int iround = SkMulDiv255Round(a, b);
+ int itrunc = SkMulDiv255Trunc(a, b);
+
+ REPORTER_ASSERT(reporter, iround == round);
+ REPORTER_ASSERT(reporter, itrunc == trunc);
+
+ REPORTER_ASSERT(reporter, itrunc <= iround);
+ REPORTER_ASSERT(reporter, iround <= a);
+ REPORTER_ASSERT(reporter, iround <= b);
+ }
+ }
+#endif
+}
+
+static void TestMath(skiatest::Reporter* reporter) {
+ int i;
+ int32_t x;
+ SkRandom rand;
+
+ // these should not assert
+ SkToS8(127); SkToS8(-128); SkToU8(255);
+ SkToS16(32767); SkToS16(-32768); SkToU16(65535);
+ SkToS32(2*1024*1024); SkToS32(-2*1024*1024); SkToU32(4*1024*1024);
+
+ // these should assert
+#if 0
+ SkToS8(128);
+ SkToS8(-129);
+ SkToU8(256);
+ SkToU8(-5);
+
+ SkToS16(32768);
+ SkToS16(-32769);
+ SkToU16(65536);
+ SkToU16(-5);
+
+ if (sizeof(size_t) > 4) {
+ SkToS32(4*1024*1024);
+ SkToS32(-4*1024*1024);
+ SkToU32(5*1024*1024);
+ SkToU32(-5);
+ }
+#endif
+
+ test_muldiv255(reporter);
+
+ {
+ SkScalar x = SK_ScalarNaN;
+ REPORTER_ASSERT(reporter, SkScalarIsNaN(x));
+ }
+
+ for (i = 1; i <= 10; i++) {
+ x = SkCubeRootBits(i*i*i, 11);
+ REPORTER_ASSERT(reporter, x == i);
+ }
+
+ REPORTER_ASSERT(reporter, !"test the reporter");
+
+ x = SkFixedSqrt(SK_Fixed1);
+ REPORTER_ASSERT(reporter, x == SK_Fixed1);
+ x = SkFixedSqrt(SK_Fixed1/4);
+ REPORTER_ASSERT(reporter, x == SK_Fixed1/2);
+ x = SkFixedSqrt(SK_Fixed1*4);
+ REPORTER_ASSERT(reporter, x == SK_Fixed1*2);
+
+ x = SkFractSqrt(SK_Fract1);
+ REPORTER_ASSERT(reporter, x == SK_Fract1);
+ x = SkFractSqrt(SK_Fract1/4);
+ REPORTER_ASSERT(reporter, x == SK_Fract1/2);
+ x = SkFractSqrt(SK_Fract1/16);
+ REPORTER_ASSERT(reporter, x == SK_Fract1/4);
+
+ for (i = 1; i < 100; i++) {
+ x = SkFixedSqrt(SK_Fixed1 * i * i);
+ REPORTER_ASSERT(reporter, x == SK_Fixed1 * i);
+ }
+
+ for (i = 0; i < 1000; i++) {
+ int value = rand.nextS16();
+ int max = rand.nextU16();
+
+ int clamp = SkClampMax(value, max);
+ int clamp2 = value < 0 ? 0 : (value > max ? max : value);
+ REPORTER_ASSERT(reporter, clamp == clamp2);
+ }
+
+ for (i = 0; i < 100000; i++) {
+ SkPoint p;
+
+ p.setLength(rand.nextS(), rand.nextS(), SK_Scalar1);
+ check_length(reporter, p, SK_Scalar1);
+ p.setLength(rand.nextS() >> 13, rand.nextS() >> 13, SK_Scalar1);
+ check_length(reporter, p, SK_Scalar1);
+ }
+
+ {
+ SkFixed result = SkFixedDiv(100, 100);
+ REPORTER_ASSERT(reporter, result == SK_Fixed1);
+ result = SkFixedDiv(1, SK_Fixed1);
+ REPORTER_ASSERT(reporter, result == 1);
+ }
+
+#ifdef SK_CAN_USE_FLOAT
+ unittest_fastfloat(reporter);
+#endif
+
+#ifdef SkLONGLONG
+ for (i = 0; i < 100000; i++) {
+ SkFixed numer = rand.nextS();
+ SkFixed denom = rand.nextS();
+ SkFixed result = SkFixedDiv(numer, denom);
+ SkLONGLONG check = ((SkLONGLONG)numer << 16) / denom;
+
+ (void)SkCLZ(numer);
+ (void)SkCLZ(denom);
+
+ REPORTER_ASSERT(reporter, result != (SkFixed)SK_NaN32);
+ if (check > SK_MaxS32) {
+ check = SK_MaxS32;
+ } else if (check < -SK_MaxS32) {
+ check = SK_MinS32;
+ }
+ REPORTER_ASSERT(reporter, result == (int32_t)check);
+
+ result = SkFractDiv(numer, denom);
+ check = ((SkLONGLONG)numer << 30) / denom;
+
+ REPORTER_ASSERT(reporter, result != (SkFixed)SK_NaN32);
+ if (check > SK_MaxS32) {
+ check = SK_MaxS32;
+ } else if (check < -SK_MaxS32) {
+ check = SK_MinS32;
+ }
+ REPORTER_ASSERT(reporter, result == (int32_t)check);
+
+ // make them <= 2^24, so we don't overflow in fixmul
+ numer = numer << 8 >> 8;
+ denom = denom << 8 >> 8;
+
+ result = SkFixedMul(numer, denom);
+ SkFixed r2 = symmetric_fixmul(numer, denom);
+ // SkASSERT(result == r2);
+
+ result = SkFixedMul(numer, numer);
+ r2 = SkFixedSquare(numer);
+ REPORTER_ASSERT(reporter, result == r2);
+
+#ifdef SK_CAN_USE_FLOAT
+ if (numer >= 0 && denom >= 0) {
+ SkFixed mean = SkFixedMean(numer, denom);
+ float fm = sk_float_sqrt(sk_float_abs(SkFixedToFloat(numer) * SkFixedToFloat(denom)));
+ SkFixed mean2 = SkFloatToFixed(fm);
+ int diff = SkAbs32(mean - mean2);
+ REPORTER_ASSERT(reporter, diff <= 1);
+ }
+
+ {
+ SkFixed mod = SkFixedMod(numer, denom);
+ float n = SkFixedToFloat(numer);
+ float d = SkFixedToFloat(denom);
+ float m = sk_float_mod(n, d);
+ REPORTER_ASSERT(reporter, mod == 0 || (mod < 0) == (m < 0)); // ensure the same sign
+ int diff = SkAbs32(mod - SkFloatToFixed(m));
+ REPORTER_ASSERT(reporter, (diff >> 7) == 0);
+ }
+#endif
+ }
+#endif
+
+#ifdef SK_CAN_USE_FLOAT
+ for (i = 0; i < 100000; i++) {
+ SkFract x = rand.nextU() >> 1;
+ double xx = (double)x / SK_Fract1;
+ SkFract xr = SkFractSqrt(x);
+ SkFract check = SkFloatToFract(sqrt(xx));
+ REPORTER_ASSERT(reporter, xr == check || xr == check-1 || xr == check+1);
+
+ xr = SkFixedSqrt(x);
+ xx = (double)x / SK_Fixed1;
+ check = SkFloatToFixed(sqrt(xx));
+ REPORTER_ASSERT(reporter, xr == check || xr == check-1);
+
+ xr = SkSqrt32(x);
+ xx = (double)x;
+ check = (int32_t)sqrt(xx);
+ REPORTER_ASSERT(reporter, xr == check || xr == check-1);
+ }
+#endif
+
+#if !defined(SK_SCALAR_IS_FLOAT) && defined(SK_CAN_USE_FLOAT)
+ {
+ SkFixed s, c;
+ s = SkFixedSinCos(0, &c);
+ REPORTER_ASSERT(reporter, s == 0);
+ REPORTER_ASSERT(reporter, c == SK_Fixed1);
+ }
+
+ int maxDiff = 0;
+ for (i = 0; i < 10000; i++) {
+ SkFixed rads = rand.nextS() >> 10;
+ double frads = SkFixedToFloat(rads);
+
+ SkFixed s, c;
+ s = SkScalarSinCos(rads, &c);
+
+ double fs = sin(frads);
+ double fc = cos(frads);
+
+ SkFixed is = SkFloatToFixed(fs);
+ SkFixed ic = SkFloatToFixed(fc);
+
+ maxDiff = SkMax32(maxDiff, SkAbs32(is - s));
+ maxDiff = SkMax32(maxDiff, SkAbs32(ic - c));
+ }
+ SkDebugf("SinCos: maximum error = %d\n", maxDiff);
+#endif
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+namespace skiatest {
+
+ class MathTest : public Test {
+ public:
+ static Test* Factory(void*) {
+ return SkNEW(MathTest);
+ }
+
+ protected:
+ virtual void onGetName(SkString* name) {
+ name->set("Math");
+ }
+
+ virtual void onRun(Reporter* reporter) {
+ TestMath(reporter);
+ }
+ };
+
+ static TestRegistry gReg(MathTest::Factory);
+}
+