aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorMike Reed <>2009-04-02 11:11:16 -0700
committerThe Android Open Source Project <initial-contribution@android.com>2009-04-02 11:11:16 -0700
commitda3b8b285a5e3e6f344461d67e3370b27701756d (patch)
treee0c45002b54fa93a80fe90200e006433e140ea3e /tests
parent0e747d6d0a794242bd214fa44a6a179baeadfdf9 (diff)
downloadexternal_skia-da3b8b285a5e3e6f344461d67e3370b27701756d.zip
external_skia-da3b8b285a5e3e6f344461d67e3370b27701756d.tar.gz
external_skia-da3b8b285a5e3e6f344461d67e3370b27701756d.tar.bz2
AI 144248: add unittests
add android-specific work around for double-initialization of globals Automated import of CL 144248
Diffstat (limited to 'tests')
-rw-r--r--tests/Android.mk35
-rw-r--r--tests/ClipCubicTest.cpp144
-rw-r--r--tests/GeometryTest.cpp16
-rw-r--r--tests/MathTest.cpp375
-rw-r--r--tests/MatrixTest.cpp98
-rw-r--r--tests/PackBitsTest.cpp127
-rw-r--r--tests/PathMeasureTest.cpp42
-rw-r--r--tests/PathTest.cpp55
-rw-r--r--tests/Sk64Test.cpp198
-rw-r--r--tests/SortTest.cpp50
-rw-r--r--tests/SrcOverTest.cpp72
-rw-r--r--tests/StreamTest.cpp94
-rw-r--r--tests/StringTest.cpp54
-rw-r--r--tests/Test.cpp60
-rw-r--r--tests/Test.h98
-rw-r--r--tests/TestClassDef.h24
-rw-r--r--tests/TriangulationTest.cpp292
-rw-r--r--tests/UtilsTest.cpp109
-rw-r--r--tests/testmain.cpp104
19 files changed, 2047 insertions, 0 deletions
diff --git a/tests/Android.mk b/tests/Android.mk
new file mode 100644
index 0000000..11665cd
--- /dev/null
+++ b/tests/Android.mk
@@ -0,0 +1,35 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ GeometryTest.cpp \
+ MathTest.cpp \
+ MatrixTest.cpp \
+ PackBitsTest.cpp \
+ Sk64Test.cpp \
+ StringTest.cpp \
+ Test.cpp UtilsTest.cpp \
+ PathTest.cpp \
+ SrcOverTest.cpp \
+ StreamTest.cpp \
+ SortTest.cpp \
+ PathMeasureTest.cpp \
+ testmain.cpp
+
+LOCAL_C_INCLUDES := \
+ external/skia/include/core \
+ external/skia/include/effects \
+ external/skia/include/images \
+ external/skia/include/ports \
+ external/skia/include/utils \
+ external/skia/src/core
+
+LOCAL_SHARED_LIBRARIES := \
+ libcorecg \
+ libsgl
+
+LOCAL_MODULE:= test-skia-unit
+
+LOCAL_MODULE_TAGS := tests
+
+include $(BUILD_EXECUTABLE)
diff --git a/tests/ClipCubicTest.cpp b/tests/ClipCubicTest.cpp
new file mode 100644
index 0000000..905b733
--- /dev/null
+++ b/tests/ClipCubicTest.cpp
@@ -0,0 +1,144 @@
+#include "Test.h"
+
+#include "SkCubicClipper.h"
+#include "SkGeometry.h"
+
+
+static void PrintCurve(const char *name, const SkPoint crv[4]) {
+ printf("%s: %.10g, %.10g, %.10g, %.10g, %.10g, %.10g, %.10g, %.10g\n",
+ name,
+ crv[0].fX, crv[0].fY,
+ crv[1].fX, crv[1].fY,
+ crv[2].fX, crv[2].fY,
+ crv[3].fX, crv[3].fY);
+
+}
+
+
+static bool CurvesAreEqual(const SkPoint c0[4],
+ const SkPoint c1[4],
+ float tol) {
+ for (int i = 0; i < 4; i++) {
+ if (SkScalarAbs(c0[i].fX - c1[i].fX) > SkFloatToScalar(tol) ||
+ SkScalarAbs(c0[i].fY - c1[i].fY) > SkFloatToScalar(tol)
+ ) {
+ PrintCurve("c0", c0);
+ PrintCurve("c1", c1);
+ return false;
+ }
+ }
+ return true;
+}
+
+
+static SkPoint* SetCurve(float x0, float y0,
+ float x1, float y1,
+ float x2, float y2,
+ float x3, float y3,
+ SkPoint crv[4]) {
+ crv[0].fX = SkFloatToScalar(x0); crv[0].fY = SkFloatToScalar(y0);
+ crv[1].fX = SkFloatToScalar(x1); crv[1].fY = SkFloatToScalar(y1);
+ crv[2].fX = SkFloatToScalar(x2); crv[2].fY = SkFloatToScalar(y2);
+ crv[3].fX = SkFloatToScalar(x3); crv[3].fY = SkFloatToScalar(y3);
+ return crv;
+}
+
+
+static void TestCubicClipping(skiatest::Reporter* reporter) {
+ static SkPoint crv[4] = {
+ { SkFloatToScalar(0), SkFloatToScalar(0) },
+ { SkFloatToScalar(2), SkFloatToScalar(3) },
+ { SkFloatToScalar(1), SkFloatToScalar(10) },
+ { SkFloatToScalar(4), SkFloatToScalar(12) }
+ };
+
+ SkCubicClipper clipper;
+ SkPoint clipped[4], shouldbe[4];
+ SkIRect clipRect;
+ bool success;
+ const float tol = 1e-4;
+
+ // Test no clip, with plenty of room.
+ clipRect.set(-2, -2, 6, 14);
+ clipper.setClip(clipRect);
+ success = clipper.clipCubic(crv, clipped);
+ REPORTER_ASSERT(reporter, success == true);
+ REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+ 0, 0, 2, 3, 1, 10, 4, 12, shouldbe), tol));
+
+ // Test no clip, touching first point.
+ clipRect.set(-2, 0, 6, 14);
+ clipper.setClip(clipRect);
+ success = clipper.clipCubic(crv, clipped);
+ REPORTER_ASSERT(reporter, success == true);
+ REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+ 0, 0, 2, 3, 1, 10, 4, 12, shouldbe), tol));
+
+ // Test no clip, touching last point.
+ clipRect.set(-2, -2, 6, 12);
+ clipper.setClip(clipRect);
+ success = clipper.clipCubic(crv, clipped);
+ REPORTER_ASSERT(reporter, success == true);
+ REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+ 0, 0, 2, 3, 1, 10, 4, 12, shouldbe), tol));
+
+ // Test all clip.
+ clipRect.set(-2, 14, 6, 20);
+ clipper.setClip(clipRect);
+ success = clipper.clipCubic(crv, clipped);
+ REPORTER_ASSERT(reporter, success == false);
+
+ // Test clip at 1.
+ clipRect.set(-2, 1, 6, 14);
+ clipper.setClip(clipRect);
+ success = clipper.clipCubic(crv, clipped);
+ REPORTER_ASSERT(reporter, success == true);
+ REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+ 0.5126125216, 1,
+ 1.841195941, 4.337081432,
+ 1.297019958, 10.19801331,
+ 4, 12,
+ shouldbe), tol));
+
+ // Test clip at 2.
+ clipRect.set(-2, 2, 6, 14);
+ clipper.setClip(clipRect);
+ success = clipper.clipCubic(crv, clipped);
+ REPORTER_ASSERT(reporter, success == true);
+ REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+ 00.8412352204, 2,
+ 1.767683744, 5.400758266,
+ 1.55052948, 10.36701965,
+ 4, 12,
+ shouldbe), tol));
+
+ // Test clip at 11.
+ clipRect.set(-2, -2, 6, 11);
+ clipper.setClip(clipRect);
+ success = clipper.clipCubic(crv, clipped);
+ REPORTER_ASSERT(reporter, success == true);
+ REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+ 0, 0,
+ 1.742904663, 2.614356995,
+ 1.207521796, 8.266430855,
+ 3.026495695, 11,
+ shouldbe), tol));
+
+ // Test clip at 10.
+ clipRect.set(-2, -2, 6, 10);
+ clipper.setClip(clipRect);
+ success = clipper.clipCubic(crv, clipped);
+ REPORTER_ASSERT(reporter, success == true);
+ REPORTER_ASSERT(reporter, CurvesAreEqual(clipped, SetCurve(
+ 0, 0,
+ 1.551193237, 2.326789856,
+ 1.297736168, 7.059780121,
+ 2.505550385, 10,
+ shouldbe), tol));
+}
+
+
+
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("CubicClipper", CubicClippingTestClass, TestCubicClipping)
diff --git a/tests/GeometryTest.cpp b/tests/GeometryTest.cpp
new file mode 100644
index 0000000..5b05952
--- /dev/null
+++ b/tests/GeometryTest.cpp
@@ -0,0 +1,16 @@
+#include "Test.h"
+#include "SkGeometry.h"
+
+static void TestGeometry(skiatest::Reporter* reporter) {
+ SkPoint pts[3], dst[5];
+
+ pts[0].set(0, 0);
+ pts[1].set(100, 50);
+ pts[2].set(0, 100);
+
+ int count = SkChopQuadAtMaxCurvature(pts, dst);
+ REPORTER_ASSERT(reporter, count == 1 || count == 2);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Geometry", GeometryTestClass, TestGeometry)
diff --git a/tests/MathTest.cpp b/tests/MathTest.cpp
new file mode 100644
index 0000000..dddd2da
--- /dev/null
+++ b/tests/MathTest.cpp
@@ -0,0 +1,375 @@
+#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++) {
+ 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 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);
+ }
+
+ 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 prod = SkFixedToFloat(numer) * SkFixedToFloat(denom);
+ float fm = sk_float_sqrt(sk_float_abs(prod));
+ 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);
+ // ensure the same sign
+ REPORTER_ASSERT(reporter, mod == 0 || (mod < 0) == (m < 0));
+ 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
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Math", MathTestClass, TestMath)
diff --git a/tests/MatrixTest.cpp b/tests/MatrixTest.cpp
new file mode 100644
index 0000000..68e587b
--- /dev/null
+++ b/tests/MatrixTest.cpp
@@ -0,0 +1,98 @@
+#include "Test.h"
+#include "SkMatrix.h"
+
+static bool nearly_equal_scalar(SkScalar a, SkScalar b) {
+#ifdef SK_SCALAR_IS_FLOAT
+ const float tolerance = 0.000005f;
+#else
+ const int32_t tolerance = 3;
+#endif
+
+ return SkScalarAbs(a - b) <= tolerance;
+}
+
+static bool nearly_equal(const SkMatrix& a, const SkMatrix& b) {
+ for (int i = 0; i < 9; i++) {
+ if (!nearly_equal_scalar(a[i], b[i])) {
+ printf("not equal %g %g\n", a[i], b[i]);
+ return false;
+ }
+ }
+ return true;
+}
+
+static bool is_identity(const SkMatrix& m) {
+ SkMatrix identity;
+ identity.reset();
+ return nearly_equal(m, identity);
+}
+
+void TestMatrix(skiatest::Reporter* reporter) {
+ SkMatrix mat, inverse, iden1, iden2;
+
+ mat.reset();
+ mat.setTranslate(SK_Scalar1, SK_Scalar1);
+ mat.invert(&inverse);
+ iden1.setConcat(mat, inverse);
+ REPORTER_ASSERT(reporter, is_identity(iden1));
+
+ mat.setScale(SkIntToScalar(2), SkIntToScalar(2));
+ mat.invert(&inverse);
+ iden1.setConcat(mat, inverse);
+ REPORTER_ASSERT(reporter, is_identity(iden1));
+
+ mat.setScale(SK_Scalar1/2, SK_Scalar1/2);
+ mat.invert(&inverse);
+ iden1.setConcat(mat, inverse);
+ REPORTER_ASSERT(reporter, is_identity(iden1));
+
+ mat.setScale(SkIntToScalar(3), SkIntToScalar(5), SkIntToScalar(20), 0);
+ mat.postRotate(SkIntToScalar(25));
+ REPORTER_ASSERT(reporter, mat.invert(NULL));
+ mat.invert(&inverse);
+ iden1.setConcat(mat, inverse);
+ REPORTER_ASSERT(reporter, is_identity(iden1));
+ iden2.setConcat(inverse, mat);
+ REPORTER_ASSERT(reporter, is_identity(iden2));
+
+ // rectStaysRect test
+ {
+ static const struct {
+ SkScalar m00, m01, m10, m11;
+ bool mStaysRect;
+ }
+ gRectStaysRectSamples[] = {
+ { 0, 0, 0, 0, false },
+ { 0, 0, 0, SK_Scalar1, false },
+ { 0, 0, SK_Scalar1, 0, false },
+ { 0, 0, SK_Scalar1, SK_Scalar1, false },
+ { 0, SK_Scalar1, 0, 0, false },
+ { 0, SK_Scalar1, 0, SK_Scalar1, false },
+ { 0, SK_Scalar1, SK_Scalar1, 0, true },
+ { 0, SK_Scalar1, SK_Scalar1, SK_Scalar1, false },
+ { SK_Scalar1, 0, 0, 0, false },
+ { SK_Scalar1, 0, 0, SK_Scalar1, true },
+ { SK_Scalar1, 0, SK_Scalar1, 0, false },
+ { SK_Scalar1, 0, SK_Scalar1, SK_Scalar1, false },
+ { SK_Scalar1, SK_Scalar1, 0, 0, false },
+ { SK_Scalar1, SK_Scalar1, 0, SK_Scalar1, false },
+ { SK_Scalar1, SK_Scalar1, SK_Scalar1, 0, false },
+ { SK_Scalar1, SK_Scalar1, SK_Scalar1, SK_Scalar1, false }
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gRectStaysRectSamples); i++) {
+ SkMatrix m;
+
+ m.reset();
+ m.set(SkMatrix::kMScaleX, gRectStaysRectSamples[i].m00);
+ m.set(SkMatrix::kMSkewX, gRectStaysRectSamples[i].m01);
+ m.set(SkMatrix::kMSkewY, gRectStaysRectSamples[i].m10);
+ m.set(SkMatrix::kMScaleY, gRectStaysRectSamples[i].m11);
+ REPORTER_ASSERT(reporter,
+ m.rectStaysRect() == gRectStaysRectSamples[i].mStaysRect);
+ }
+ }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Matrix", MatrixTestClass, TestMatrix)
diff --git a/tests/PackBitsTest.cpp b/tests/PackBitsTest.cpp
new file mode 100644
index 0000000..729467e
--- /dev/null
+++ b/tests/PackBitsTest.cpp
@@ -0,0 +1,127 @@
+#include "Test.h"
+#include "SkPackBits.h"
+
+static const uint16_t gTest0[] = { 0, 0, 1, 1 };
+static const uint16_t gTest1[] = { 1, 2, 3, 4, 5, 6 };
+static const uint16_t gTest2[] = { 0, 0, 0, 1, 2, 3, 3, 3 };
+static const uint16_t gTest3[] = { 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 0, 0, 1 };
+
+#include "SkRandom.h"
+static SkRandom gRand;
+static void rand_fill(uint16_t buffer[], int count) {
+ for (int i = 0; i < count; i++)
+ buffer[i] = (uint16_t)gRand.nextU();
+}
+
+static void test_pack16(skiatest::Reporter* reporter) {
+ static const struct {
+ const uint16_t* fSrc;
+ int fCount;
+ } gTests[] = {
+ { gTest0, SK_ARRAY_COUNT(gTest0) },
+ { gTest1, SK_ARRAY_COUNT(gTest1) },
+ { gTest2, SK_ARRAY_COUNT(gTest2) },
+ { gTest3, SK_ARRAY_COUNT(gTest3) }
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gTests); i++) {
+ uint8_t dst[100];
+ size_t dstSize = SkPackBits::Pack16(gTests[i].fSrc,
+ gTests[i].fCount, dst);
+ uint16_t src[100];
+ int srcCount = SkPackBits::Unpack16(dst, dstSize, src);
+ bool match = gTests[i].fCount == srcCount && memcmp(gTests[i].fSrc, src,
+ gTests[i].fCount * sizeof(uint16_t)) == 0;
+ REPORTER_ASSERT(reporter, match);
+ }
+
+ for (int n = 1000; n; n--) {
+ size_t size = 50;
+ uint16_t src[100], src2[100];
+ uint8_t dst[200];
+ rand_fill(src, size);
+
+ size_t dstSize = SkPackBits::Pack16(src, size, dst);
+ size_t maxSize = SkPackBits::ComputeMaxSize16(size);
+ REPORTER_ASSERT(reporter, maxSize >= dstSize);
+
+ size_t srcCount = SkPackBits::Unpack16(dst, dstSize, src2);
+ REPORTER_ASSERT(reporter, size == srcCount);
+ bool match = memcmp(src, src2, size * sizeof(uint16_t)) == 0;
+ REPORTER_ASSERT(reporter, match);
+ }
+}
+
+static const uint8_t gTest80[] = { 0, 0, 1, 1 };
+static const uint8_t gTest81[] = { 1, 2, 3, 4, 5, 6 };
+static const uint8_t gTest82[] = { 0, 0, 0, 1, 2, 3, 3, 3 };
+static const uint8_t gTest83[] = { 0, 0, 0, 0, 0, 0, 1, 2, 3, 3, 3, 0, 0, 1 };
+static const uint8_t gTest84[] = { 1, 0, 3, 0, 0, 0, 2, 1, 1, 2 };
+
+static void rand_fill(uint8_t buffer[], int count) {
+ for (int i = 0; i < count; i++)
+ buffer[i] = (uint8_t)((gRand.nextU() >> 8) & 0x3);
+}
+
+static void test_pack8(skiatest::Reporter* reporter) {
+ static const struct {
+ const uint8_t* fSrc;
+ int fCount;
+ } gTests[] = {
+ { gTest80, SK_ARRAY_COUNT(gTest80) },
+ { gTest81, SK_ARRAY_COUNT(gTest81) },
+ { gTest82, SK_ARRAY_COUNT(gTest82) },
+ { gTest83, SK_ARRAY_COUNT(gTest83) },
+ { gTest84, SK_ARRAY_COUNT(gTest84) }
+ };
+
+ for (size_t i = 4; i < SK_ARRAY_COUNT(gTests); i++) {
+ uint8_t dst[100];
+ size_t maxSize = SkPackBits::ComputeMaxSize8(gTests[i].fCount);
+ size_t dstSize = SkPackBits::Pack8(gTests[i].fSrc,
+ gTests[i].fCount, dst);
+ REPORTER_ASSERT(reporter, dstSize <= maxSize);
+ uint8_t src[100];
+ int srcCount = SkPackBits::Unpack8(dst, dstSize, src);
+ bool match = gTests[i].fCount == srcCount &&
+ memcmp(gTests[i].fSrc, src,
+ gTests[i].fCount * sizeof(uint8_t)) == 0;
+ REPORTER_ASSERT(reporter, match);
+ }
+
+ for (size_t size = 1; size <= 512; size += 1) {
+ for (int n = 200; n; n--) {
+ uint8_t src[600], src2[600];
+ uint8_t dst[600];
+ rand_fill(src, size);
+
+ size_t dstSize = SkPackBits::Pack8(src, size, dst);
+ size_t maxSize = SkPackBits::ComputeMaxSize8(size);
+ REPORTER_ASSERT(reporter, maxSize >= dstSize);
+
+ size_t srcCount = SkPackBits::Unpack8(dst, dstSize, src2);
+ REPORTER_ASSERT(reporter, size == srcCount);
+ bool match = memcmp(src, src2, size * sizeof(uint8_t)) == 0;
+ REPORTER_ASSERT(reporter, match);
+
+ for (int j = 0; j < 200; j++) {
+ size_t skip = gRand.nextU() % size;
+ size_t write = gRand.nextU() % size;
+ if (skip + write > size) {
+ write = size - skip;
+ }
+ SkPackBits::Unpack8(src, skip, write, dst);
+ bool match = memcmp(src, src2 + skip, write) == 0;
+ REPORTER_ASSERT(reporter, match);
+ }
+ }
+ }
+}
+
+static void TestPackBits(skiatest::Reporter* reporter) {
+ test_pack8(reporter);
+ test_pack16(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("PackBits", PackBitsTestClass, TestPackBits)
diff --git a/tests/PathMeasureTest.cpp b/tests/PathMeasureTest.cpp
new file mode 100644
index 0000000..de18764
--- /dev/null
+++ b/tests/PathMeasureTest.cpp
@@ -0,0 +1,42 @@
+#include "Test.h"
+#include "SkPathMeasure.h"
+
+static void TestPathMeasure(skiatest::Reporter* reporter) {
+ SkPath path;
+
+ path.moveTo(0, 0);
+ path.lineTo(SK_Scalar1, 0);
+ path.lineTo(SK_Scalar1, SK_Scalar1);
+ path.lineTo(0, SK_Scalar1);
+
+ SkPathMeasure meas(path, true);
+ SkScalar length = meas.getLength();
+ SkASSERT(length == SK_Scalar1*4);
+
+ path.reset();
+ path.moveTo(0, 0);
+ path.lineTo(SK_Scalar1*3, SK_Scalar1*4);
+ meas.setPath(&path, false);
+ length = meas.getLength();
+ REPORTER_ASSERT(reporter, length == SK_Scalar1*5);
+
+ path.reset();
+ path.addCircle(0, 0, SK_Scalar1);
+ meas.setPath(&path, true);
+ length = meas.getLength();
+// SkDebugf("circle arc-length = %g\n", length);
+
+ for (int i = 0; i < 8; i++) {
+ SkScalar d = length * i / 8;
+ SkPoint p;
+ SkVector v;
+ meas.getPosTan(d, &p, &v);
+#if 0
+ SkDebugf("circle arc-length=%g, pos[%g %g] tan[%g %g]\n",
+ d, p.fX, p.fY, v.fX, v.fY);
+#endif
+ }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("PathMeasure", PathMeasureTestClass, TestPathMeasure)
diff --git a/tests/PathTest.cpp b/tests/PathTest.cpp
new file mode 100644
index 0000000..c17fa45
--- /dev/null
+++ b/tests/PathTest.cpp
@@ -0,0 +1,55 @@
+#include "Test.h"
+#include "SkPath.h"
+
+static void TestPath(skiatest::Reporter* reporter) {
+ SkPath p, p2;
+ SkRect bounds, bounds2;
+
+ REPORTER_ASSERT(reporter, p.isEmpty());
+ REPORTER_ASSERT(reporter, p.getFillType() == SkPath::kWinding_FillType);
+ REPORTER_ASSERT(reporter, !p.isInverseFillType());
+ REPORTER_ASSERT(reporter, p == p2);
+ REPORTER_ASSERT(reporter, !(p != p2));
+
+ REPORTER_ASSERT(reporter, p.getBounds().isEmpty());
+
+ bounds.set(0, 0, SK_Scalar1, SK_Scalar1);
+ p.addRect(bounds);
+ REPORTER_ASSERT(reporter, bounds == p.getBounds());
+
+ REPORTER_ASSERT(reporter, p != p2);
+ REPORTER_ASSERT(reporter, !(p == p2));
+
+ // does getPoints return the right result
+ REPORTER_ASSERT(reporter, p.getPoints(NULL, 5) == 4);
+ SkPoint pts[4];
+ int count = p.getPoints(pts, 4);
+ REPORTER_ASSERT(reporter, count == 4);
+ bounds2.set(pts, 4);
+ REPORTER_ASSERT(reporter, bounds == bounds2);
+
+ bounds.offset(SK_Scalar1*3, SK_Scalar1*4);
+ p.offset(SK_Scalar1*3, SK_Scalar1*4);
+ REPORTER_ASSERT(reporter, bounds == p.getBounds());
+
+#if 0 // isRect needs to be implemented
+ REPORTER_ASSERT(reporter, p.isRect(NULL));
+ bounds.setEmpty();
+ REPORTER_ASSERT(reporter, p.isRect(&bounds2));
+ REPORTER_ASSERT(reporter, bounds == bounds2);
+
+ // now force p to not be a rect
+ bounds.set(0, 0, SK_Scalar1/2, SK_Scalar1/2);
+ p.addRect(bounds);
+ REPORTER_ASSERT(reporter, !p.isRect(NULL));
+#endif
+
+ SkPoint pt;
+
+ p.moveTo(SK_Scalar1, 0);
+ p.getLastPt(&pt);
+ REPORTER_ASSERT(reporter, pt.fX == SK_Scalar1);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Path", PathTestClass, TestPath)
diff --git a/tests/Sk64Test.cpp b/tests/Sk64Test.cpp
new file mode 100644
index 0000000..9fb49eb
--- /dev/null
+++ b/tests/Sk64Test.cpp
@@ -0,0 +1,198 @@
+#include "Test.h"
+#include "SkRandom.h"
+#include <math.h>
+
+struct BoolTable {
+ int8_t zero, pos, neg, toBool, sign;
+};
+
+static void bool_table_test(skiatest::Reporter* reporter,
+ const Sk64& a, const BoolTable& table)
+{
+ REPORTER_ASSERT(reporter, a.isZero() != a.nonZero());
+
+ REPORTER_ASSERT(reporter, !a.isZero() == !table.zero);
+ REPORTER_ASSERT(reporter, !a.isPos() == !table.pos);
+ REPORTER_ASSERT(reporter, !a.isNeg() == !table.neg);
+ REPORTER_ASSERT(reporter, a.getSign() == table.sign);
+}
+
+#ifdef SkLONGLONG
+ static SkLONGLONG asLL(const Sk64& a)
+ {
+ return ((SkLONGLONG)a.fHi << 32) | a.fLo;
+ }
+#endif
+
+static void TestSk64(skiatest::Reporter* reporter) {
+ enum BoolTests {
+ kZero_BoolTest,
+ kPos_BoolTest,
+ kNeg_BoolTest
+ };
+ static const BoolTable gBoolTable[] = {
+ { 1, 0, 0, 0, 0 },
+ { 0, 1, 0, 1, 1 },
+ { 0, 0, 1, 1, -1 }
+ };
+
+ Sk64 a, b, c;
+
+ a.fHi = a.fLo = 0;
+ b.set(0);
+ c.setZero();
+ REPORTER_ASSERT(reporter, a == b);
+ REPORTER_ASSERT(reporter, a == c);
+ bool_table_test(reporter, a, gBoolTable[kZero_BoolTest]);
+
+ a.fHi = 0; a.fLo = 5;
+ b.set(5);
+ REPORTER_ASSERT(reporter, a == b);
+ REPORTER_ASSERT(reporter, a.is32() && a.get32() == 5 && !a.is64());
+ bool_table_test(reporter, a, gBoolTable[kPos_BoolTest]);
+
+ a.fHi = -1; a.fLo = (uint32_t)-5;
+ b.set(-5);
+ REPORTER_ASSERT(reporter, a == b);
+ REPORTER_ASSERT(reporter, a.is32() && a.get32() == -5 && !a.is64());
+ bool_table_test(reporter, a, gBoolTable[kNeg_BoolTest]);
+
+ a.setZero();
+ b.set(6);
+ c.set(-6);
+ REPORTER_ASSERT(reporter, a != b && b != c && a != c);
+ REPORTER_ASSERT(reporter, !(a == b) && !(a == b) && !(a == b));
+ REPORTER_ASSERT(reporter, a < b && b > a && a <= b && b >= a);
+ REPORTER_ASSERT(reporter, c < a && a > c && c <= a && a >= c);
+ REPORTER_ASSERT(reporter, c < b && b > c && c <= b && b >= c);
+
+ // Now test add/sub
+
+ SkRandom rand;
+ int i;
+
+ for (i = 0; i < 1000; i++)
+ {
+ int aa = rand.nextS() >> 1;
+ int bb = rand.nextS() >> 1;
+ a.set(aa);
+ b.set(bb);
+ REPORTER_ASSERT(reporter, a.get32() == aa && b.get32() == bb);
+ c = a; c.add(bb);
+ REPORTER_ASSERT(reporter, c.get32() == aa + bb);
+ c = a; c.add(-bb);
+ REPORTER_ASSERT(reporter, c.get32() == aa - bb);
+ c = a; c.add(b);
+ REPORTER_ASSERT(reporter, c.get32() == aa + bb);
+ c = a; c.sub(b);
+ REPORTER_ASSERT(reporter, c.get32() == aa - bb);
+ }
+
+#ifdef SkLONGLONG
+ for (i = 0; i < 1000; i++)
+ {
+ rand.next64(&a); //a.fHi >>= 1; // avoid overflow
+ rand.next64(&b); //b.fHi >>= 1; // avoid overflow
+
+ if (!(i & 3)) // want to explicitly test these cases
+ {
+ a.fLo = 0;
+ b.fLo = 0;
+ }
+ else if (!(i & 7)) // want to explicitly test these cases
+ {
+ a.fHi = 0;
+ b.fHi = 0;
+ }
+
+ SkLONGLONG aa = asLL(a);
+ SkLONGLONG bb = asLL(b);
+
+ REPORTER_ASSERT(reporter, (a < b) == (aa < bb));
+ REPORTER_ASSERT(reporter, (a <= b) == (aa <= bb));
+ REPORTER_ASSERT(reporter, (a > b) == (aa > bb));
+ REPORTER_ASSERT(reporter, (a >= b) == (aa >= bb));
+ REPORTER_ASSERT(reporter, (a == b) == (aa == bb));
+ REPORTER_ASSERT(reporter, (a != b) == (aa != bb));
+
+ c = a; c.add(b);
+ REPORTER_ASSERT(reporter, asLL(c) == aa + bb);
+ c = a; c.sub(b);
+ REPORTER_ASSERT(reporter, asLL(c) == aa - bb);
+ c = a; c.rsub(b);
+ REPORTER_ASSERT(reporter, asLL(c) == bb - aa);
+ c = a; c.negate();
+ REPORTER_ASSERT(reporter, asLL(c) == -aa);
+
+ int bits = rand.nextU() & 63;
+ c = a; c.shiftLeft(bits);
+ REPORTER_ASSERT(reporter, asLL(c) == (aa << bits));
+ c = a; c.shiftRight(bits);
+ REPORTER_ASSERT(reporter, asLL(c) == (aa >> bits));
+ c = a; c.roundRight(bits);
+
+ SkLONGLONG tmp;
+
+ tmp = aa;
+ if (bits > 0)
+ tmp += (SkLONGLONG)1 << (bits - 1);
+ REPORTER_ASSERT(reporter, asLL(c) == (tmp >> bits));
+
+ c.setMul(a.fHi, b.fHi);
+ tmp = (SkLONGLONG)a.fHi * b.fHi;
+ REPORTER_ASSERT(reporter, asLL(c) == tmp);
+ }
+
+
+ for (i = 0; i < 100000; i++)
+ {
+ Sk64 wide;
+ int32_t denom = rand.nextS();
+
+ while (denom == 0)
+ denom = rand.nextS();
+ wide.setMul(rand.nextS(), rand.nextS());
+ SkLONGLONG check = wide.getLongLong();
+
+ wide.div(denom, Sk64::kTrunc_DivOption);
+ check /= denom;
+ SkLONGLONG w = wide.getLongLong();
+
+ REPORTER_ASSERT(reporter, check == w);
+
+#ifdef SK_CAN_USE_FLOAT
+ wide.setMul(rand.nextS(), rand.nextS());
+ wide.abs();
+ denom = wide.getSqrt();
+ int32_t ck = (int32_t)sqrt((double)wide.getLongLong());
+ int diff = denom - ck;
+ REPORTER_ASSERT(reporter, SkAbs32(diff) <= 1);
+
+ wide.setMul(rand.nextS(), rand.nextS());
+ Sk64 dwide;
+ dwide.setMul(rand.nextS(), rand.nextS());
+ SkFixed fixdiv = wide.getFixedDiv(dwide);
+ double dnumer = (double)wide.getLongLong();
+ double ddenom = (double)dwide.getLongLong();
+ double ddiv = dnumer / ddenom;
+ SkFixed dfixdiv;
+ if (ddiv >= (double)SK_MaxS32 / (double)SK_Fixed1)
+ dfixdiv = SK_MaxS32;
+ else if (ddiv <= -(double)SK_MaxS32 / (double)SK_Fixed1)
+ dfixdiv = SK_MinS32;
+ else
+ dfixdiv = SkFloatToFixed(dnumer / ddenom);
+ diff = fixdiv - dfixdiv;
+
+ if (SkAbs32(diff) > 1) {
+ SkDebugf(" %d === numer %g denom %g div %g xdiv %x fxdiv %x\n",
+ i, dnumer, ddenom, ddiv, dfixdiv, fixdiv);
+ }
+ REPORTER_ASSERT(reporter, SkAbs32(diff) <= 1);
+#endif
+ }
+#endif
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Sk64", Sk64TestClass, TestSk64)
diff --git a/tests/SortTest.cpp b/tests/SortTest.cpp
new file mode 100644
index 0000000..2ca5553
--- /dev/null
+++ b/tests/SortTest.cpp
@@ -0,0 +1,50 @@
+#include "Test.h"
+#include "SkRandom.h"
+#include "SkTSearch.h"
+#include "SkTSort.h"
+
+extern "C" {
+ int compare_int(const void* a, const void* b) {
+ return *(const int*)a - *(const int*)b;
+ }
+}
+
+static void rand_array(SkRandom& rand, int array[], int n) {
+ for (int j = 0; j < n; j++) {
+ array[j] = rand.nextS() & 0xFF;
+ }
+}
+
+static void check_sort(skiatest::Reporter* reporter, const char label[],
+ const int array[], int n) {
+ for (int j = 1; j < n; j++) {
+ if (array[j-1] > array[j]) {
+ SkString str;
+ str.printf("%sSort [%d] failed %d %d", label, n,
+ array[j-1], array[j]);
+ reporter->reportFailed(str);
+ }
+ }
+}
+
+static void TestSort(skiatest::Reporter* reporter) {
+ int array[500];
+ SkRandom rand;
+
+ for (int i = 0; i < 10000; i++) {
+ int count = rand.nextRangeU(1, SK_ARRAY_COUNT(array));
+
+ rand_array(rand, array, count);
+ SkQSort(array, count, sizeof(int), compare_int);
+ check_sort(reporter, "Quick", array, count);
+
+ rand_array(rand, array, count);
+ SkTHeapSort<int>(array, count);
+ check_sort(reporter, "Heap", array, count);
+ }
+}
+
+// need tests for SkStrSearch
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Sort", SortTestClass, TestSort)
diff --git a/tests/SrcOverTest.cpp b/tests/SrcOverTest.cpp
new file mode 100644
index 0000000..497b3a8
--- /dev/null
+++ b/tests/SrcOverTest.cpp
@@ -0,0 +1,72 @@
+#include "Test.h"
+#include "SkColorPriv.h"
+#include "SkXfermode.h"
+
+// our std SkAlpha255To256
+static int test_srcover0(unsigned dst, unsigned alpha) {
+ return alpha + SkAlphaMul(dst, SkAlpha255To256(255 - alpha));
+}
+
+// faster hack +1
+static int test_srcover1(unsigned dst, unsigned alpha) {
+ return alpha + SkAlphaMul(dst, 256 - alpha);
+}
+
+// slower "correct"
+static int test_srcover2(unsigned dst, unsigned alpha) {
+ return alpha + SkMulDiv255Round(dst, 255 - alpha);
+}
+
+static void test_srcover_hack(skiatest::Reporter* reporter) {
+ /* Here's the idea. Can we ensure that when we blend on top of an opaque
+ dst, that the result always stay's opaque (i.e. exactly 255)?
+ */
+
+ unsigned i;
+ int opaqueCounter0 = 0;
+ int opaqueCounter1 = 0;
+ int opaqueCounter2 = 0;
+ for (i = 0; i <= 255; i++) {
+ unsigned result0 = test_srcover0(0xFF, i);
+ unsigned result1 = test_srcover1(0xFF, i);
+ unsigned result2 = test_srcover2(0xFF, i);
+ opaqueCounter0 += (result0 == 0xFF);
+ opaqueCounter1 += (result1 == 0xFF);
+ opaqueCounter2 += (result2 == 0xFF);
+ }
+#if 0
+ SkDebugf("---- opaque test: [%d %d %d]\n",
+ opaqueCounter0, opaqueCounter1, opaqueCounter2);
+#endif
+ // we acknowledge that technique0 does not always return opaque
+ REPORTER_ASSERT(reporter, opaqueCounter0 == 129);
+ REPORTER_ASSERT(reporter, opaqueCounter1 == 256);
+ REPORTER_ASSERT(reporter, opaqueCounter2 == 256);
+
+ // Now ensure that we never over/underflow a byte
+ for (i = 0; i <= 255; i++) {
+ for (unsigned dst = 0; dst <= 255; dst++) {
+ unsigned r0 = test_srcover0(dst, i);
+ unsigned r1 = test_srcover1(dst, i);
+ unsigned r2 = test_srcover2(dst, i);
+ unsigned max = SkMax32(dst, i);
+ // ignore the known failure
+ if (dst != 255) {
+ REPORTER_ASSERT(reporter, r0 <= 255 && r0 >= max);
+ }
+ REPORTER_ASSERT(reporter, r1 <= 255 && r1 >= max);
+ REPORTER_ASSERT(reporter, r2 <= 255 && r2 >= max);
+
+#if 0
+ // this shows where r1 (faster) differs from r2 (more exact)
+ if (r1 != r2) {
+ SkDebugf("--- dst=%d i=%d r1=%d r2=%d exact=%g\n",
+ dst, i, r1, r2, i + dst - dst*i/255.0f);
+ }
+#endif
+ }
+ }
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("SrcOver", SrcOverTestClass, test_srcover_hack)
diff --git a/tests/StreamTest.cpp b/tests/StreamTest.cpp
new file mode 100644
index 0000000..e62f2ed
--- /dev/null
+++ b/tests/StreamTest.cpp
@@ -0,0 +1,94 @@
+#include "Test.h"
+#include "SkRandom.h"
+#include "SkStream.h"
+
+#define MAX_SIZE (256 * 1024)
+
+static void random_fill(SkRandom& rand, void* buffer, size_t size) {
+ char* p = (char*)buffer;
+ char* stop = p + size;
+ while (p < stop) {
+ *p++ = (char)(rand.nextU() >> 8);
+ }
+}
+
+static void test_buffer(skiatest::Reporter* reporter) {
+ SkRandom rand;
+ SkAutoMalloc am(MAX_SIZE * 2);
+ char* storage = (char*)am.get();
+ char* storage2 = storage + MAX_SIZE;
+
+ random_fill(rand, storage, MAX_SIZE);
+
+ for (int sizeTimes = 0; sizeTimes < 100; sizeTimes++) {
+ int size = rand.nextU() % MAX_SIZE;
+ if (size == 0) {
+ size = MAX_SIZE;
+ }
+ for (int times = 0; times < 100; times++) {
+ int bufferSize = 1 + (rand.nextU() & 0xFFFF);
+ SkMemoryStream mstream(storage, size);
+ SkBufferStream bstream(&mstream, bufferSize);
+
+ int bytesRead = 0;
+ while (bytesRead < size) {
+ int s = 17 + (rand.nextU() & 0xFFFF);
+ int ss = bstream.read(storage2, s);
+ REPORTER_ASSERT(reporter, ss > 0 && ss <= s);
+ REPORTER_ASSERT(reporter, bytesRead + ss <= size);
+ REPORTER_ASSERT(reporter,
+ memcmp(storage + bytesRead, storage2, ss) == 0);
+ bytesRead += ss;
+ }
+ REPORTER_ASSERT(reporter, bytesRead == size);
+ }
+ }
+}
+
+static void TestRStream(skiatest::Reporter* reporter) {
+ static const char s[] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ char copy[sizeof(s)];
+ SkRandom rand;
+
+ for (int i = 0; i < 65; i++) {
+ char* copyPtr = copy;
+ SkMemoryStream mem(s, sizeof(s));
+ SkBufferStream buff(&mem, i);
+
+ do {
+ copyPtr += buff.read(copyPtr, rand.nextU() & 15);
+ } while (copyPtr < copy + sizeof(s));
+ REPORTER_ASSERT(reporter, copyPtr == copy + sizeof(s));
+ REPORTER_ASSERT(reporter, memcmp(s, copy, sizeof(s)) == 0);
+ }
+ test_buffer(reporter);
+}
+
+static void TestWStream(skiatest::Reporter* reporter) {
+ SkDynamicMemoryWStream ds;
+ const char s[] = "abcdefghijklmnopqrstuvwxyz";
+ int i;
+ for (i = 0; i < 100; i++) {
+ REPORTER_ASSERT(reporter, ds.write(s, 26));
+ }
+ REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
+ char* dst = new char[100 * 26 + 1];
+ dst[100*26] = '*';
+ ds.copyTo(dst);
+ REPORTER_ASSERT(reporter, dst[100*26] == '*');
+// char* p = dst;
+ for (i = 0; i < 100; i++) {
+ REPORTER_ASSERT(reporter, memcmp(&dst[i * 26], s, 26) == 0);
+ }
+ REPORTER_ASSERT(reporter, memcmp(dst, ds.getStream(), 100*26) == 0);
+ delete[] dst;
+}
+
+static void TestStreams(skiatest::Reporter* reporter) {
+ TestRStream(reporter);
+ TestWStream(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Stream", StreamTestClass, TestStreams)
diff --git a/tests/StringTest.cpp b/tests/StringTest.cpp
new file mode 100644
index 0000000..344c752
--- /dev/null
+++ b/tests/StringTest.cpp
@@ -0,0 +1,54 @@
+#include "Test.h"
+#include "SkString.h"
+
+static void TestString(skiatest::Reporter* reporter) {
+ SkString a;
+ SkString b((size_t)0);
+ SkString c("");
+ SkString d(NULL, 0);
+
+ REPORTER_ASSERT(reporter, a.isEmpty());
+ REPORTER_ASSERT(reporter, a == b && a == c && a == d);
+
+ a.set("hello");
+ b.set("hellox", 5);
+ c.set(a);
+ d.resize(5);
+ memcpy(d.writable_str(), "helloz", 5);
+
+ REPORTER_ASSERT(reporter, !a.isEmpty());
+ REPORTER_ASSERT(reporter, a.size() == 5);
+ REPORTER_ASSERT(reporter, a == b && a == c && a == d);
+ REPORTER_ASSERT(reporter, a.equals("hello", 5));
+ REPORTER_ASSERT(reporter, a.equals("hello"));
+ REPORTER_ASSERT(reporter, !a.equals("help"));
+
+ SkString e(a);
+ SkString f("hello");
+ SkString g("helloz", 5);
+
+ REPORTER_ASSERT(reporter, a == e && a == f && a == g);
+
+ b.set("world");
+ c = b;
+ REPORTER_ASSERT(reporter, a != b && a != c && b == c);
+
+ a.append(" world");
+ e.append("worldz", 5);
+ e.insert(5, " ");
+ f.set("world");
+ f.prepend("hello ");
+ REPORTER_ASSERT(reporter, a.equals("hello world") && a == e && a == f);
+
+ a.reset();
+ b.resize(0);
+ REPORTER_ASSERT(reporter, a.isEmpty() && b.isEmpty() && a == b);
+
+ a.set("a");
+ a.set("ab");
+ a.set("abc");
+ a.set("abcd");
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("String", StringTestClass, TestString)
diff --git a/tests/Test.cpp b/tests/Test.cpp
new file mode 100644
index 0000000..5b8d439
--- /dev/null
+++ b/tests/Test.cpp
@@ -0,0 +1,60 @@
+#include "Test.h"
+
+using namespace skiatest;
+
+Reporter::Reporter() {
+ this->resetReporting();
+}
+
+void Reporter::resetReporting() {
+ fCurrTest = NULL;
+ fTestCount = 0;
+ bzero(fResultCount, sizeof(fResultCount));
+}
+
+void Reporter::startTest(Test* test) {
+ SkASSERT(NULL == fCurrTest);
+ fCurrTest = test;
+ this->onStart(test);
+ fTestCount += 1;
+}
+
+void Reporter::report(const char desc[], Result result) {
+ if (NULL == desc) {
+ desc = "<no description>";
+ }
+ this->onReport(desc, result);
+ fResultCount[result] += 1;
+}
+
+void Reporter::endTest(Test* test) {
+ SkASSERT(test == fCurrTest);
+ this->onEnd(test);
+ fCurrTest = NULL;
+}
+
+///////////////////////////////////////////////////////////////////////////////
+
+Test::Test() : fReporter(NULL) {}
+
+Test::~Test() {
+ fReporter->safeUnref();
+}
+
+void Test::setReporter(Reporter* r) {
+ SkRefCnt_SafeAssign(fReporter, r);
+}
+
+const char* Test::getName() {
+ if (fName.size() == 0) {
+ this->onGetName(&fName);
+ }
+ return fName.c_str();
+}
+
+void Test::run() {
+ fReporter->startTest(this);
+ this->onRun(fReporter);
+ fReporter->endTest(this);
+}
+
diff --git a/tests/Test.h b/tests/Test.h
new file mode 100644
index 0000000..d5dc9e3
--- /dev/null
+++ b/tests/Test.h
@@ -0,0 +1,98 @@
+#ifndef skiatest_Test_DEFINED
+#define skiatest_Test_DEFINED
+
+#include "SkRefCnt.h"
+#include "SkString.h"
+#include "SkTRegistry.h"
+
+namespace skiatest {
+
+ class Test;
+
+ class Reporter : public SkRefCnt {
+ public:
+ Reporter();
+
+ enum Result {
+ kPassed, // must begin with 0
+ kFailed,
+ /////
+ kLastResult = kFailed
+ };
+
+ void resetReporting();
+ int countTests() const { return fTestCount; }
+ int countResults(Result r) {
+ SkASSERT((unsigned)r <= kLastResult);
+ return fResultCount[r];
+ }
+
+ void startTest(Test*);
+ void report(const char testDesc[], Result);
+ void endTest(Test*);
+
+ // helpers for tests
+ void assertTrue(bool cond, const char desc[]) {
+ if (!cond) {
+ this->report(desc, kFailed);
+ }
+ }
+ void assertFalse(bool cond, const char desc[]) {
+ if (cond) {
+ this->report(desc, kFailed);
+ }
+ }
+ void reportFailed(const char desc[]) {
+ this->report(desc, kFailed);
+ }
+ void reportFailed(const SkString& desc) {
+ this->report(desc.c_str(), kFailed);
+ }
+
+ protected:
+ virtual void onStart(Test*) {}
+ virtual void onReport(const char desc[], Result) {}
+ virtual void onEnd(Test*) {}
+
+ private:
+ Test* fCurrTest;
+ int fTestCount;
+ int fResultCount[kLastResult+1];
+
+ typedef SkRefCnt INHERITED;
+ };
+
+ class Test {
+ public:
+ Test();
+ virtual ~Test();
+
+ Reporter* getReporter() const { return fReporter; }
+ void setReporter(Reporter*);
+
+ const char* getName();
+ void run();
+
+ protected:
+ virtual void onGetName(SkString*) = 0;
+ virtual void onRun(Reporter*) = 0;
+
+ private:
+ Reporter* fReporter;
+ SkString fName;
+ };
+
+ typedef SkTRegistry<Test*, void*> TestRegistry;
+}
+
+#define REPORTER_ASSERT(r, cond) \
+ do { \
+ if (!(cond)) { \
+ SkString desc; \
+ desc.printf("%s:%d: %s", __FILE__, __LINE__, #cond); \
+ r->reportFailed(desc); \
+ } \
+ } while(0)
+
+
+#endif
diff --git a/tests/TestClassDef.h b/tests/TestClassDef.h
new file mode 100644
index 0000000..0773f5a
--- /dev/null
+++ b/tests/TestClassDef.h
@@ -0,0 +1,24 @@
+/* This file is meant to be included by .cpp files, so it can spew out a
+ customized class + global definition.
+
+ e.g.
+ #include "TestClassDef.h"
+ DEFINE_TESTCLASS("MyTest", MyTestClass, MyTestFunction)
+
+ where MyTestFunction is declared as
+
+ void MyTestFunction(skiatest::Reporter*)
+*/
+
+#define DEFINE_TESTCLASS(uiname, classname, function) \
+ namespace skiatest { \
+ class classname : public Test { \
+ public: \
+ static Test* Factory(void*) { return SkNEW(classname); } \
+ protected: \
+ virtual void onGetName(SkString* name) { name->set(uiname); } \
+ virtual void onRun(Reporter* reporter) { function(reporter); } \
+ }; \
+ static TestRegistry gReg(classname::Factory); \
+ }
+
diff --git a/tests/TriangulationTest.cpp b/tests/TriangulationTest.cpp
new file mode 100644
index 0000000..9d47181
--- /dev/null
+++ b/tests/TriangulationTest.cpp
@@ -0,0 +1,292 @@
+#include "Test.h"
+#include "../../src/core/SkConcaveToTriangles.h"
+#include "SkGeometry.h"
+
+static int GetIndexFromPoint(const SkPoint &pt,
+ int numPts, const SkPoint *pts) {
+ for (int i = 0; i < numPts; ++i)
+ if (pt.fX == pts[i].fX && pt.fY == pts[i].fY)
+ return i;
+ return -1;
+}
+
+
+bool gPrintTriangles = false; // Can we set this on the command line?
+
+static void PrintTriangles(const SkTDArray<SkPoint> &triangles,
+ int numPts, const SkPoint *pts,
+ skiatest::Reporter* reporter) {
+ if (gPrintTriangles) {
+ SkPoint *p = triangles.begin();
+ int n = triangles.count();
+ REPORTER_ASSERT(reporter, n % 3 == 0);
+ n /= 3;
+ printf("%d Triangles:\n{\n", n);
+ for (; n-- != 0; p += 3)
+ printf(" { {%.7g, %.7g}, {%.7g, %.7g}, {%.7g, %.7g} }, "
+ "// { %2d, %2d, %2d }\n",
+ p[0].fX, p[0].fY,
+ p[1].fX, p[1].fY,
+ p[2].fX, p[2].fY,
+ GetIndexFromPoint(p[0], numPts, pts),
+ GetIndexFromPoint(p[1], numPts, pts),
+ GetIndexFromPoint(p[2], numPts, pts));
+ printf("}\n");
+ }
+}
+
+
+static bool CompareTriangleList(int numTriangles,
+ const float refTriangles[][3][2],
+ const SkTDArray<SkPoint> &triangles) {
+ if (triangles.count() != numTriangles * 3) {
+ printf("Expected %d triangles, not %d\n",
+ numTriangles, triangles.count() / 3);
+ return false;
+ }
+ int numErrors = 0;
+ for (int i = 0; i < numTriangles; ++i) {
+ const float *r = &refTriangles[i][0][0];
+ const SkScalar *t = &triangles[i * 3].fX;
+ bool equalTriangle = true;
+ for (int j = 6; j-- != 0; r++, t++)
+ if (SkFloatToScalar(*r) != *t)
+ equalTriangle = false;
+ if (equalTriangle == false) {
+ ++numErrors;
+ printf("Triangle %d differs\n", i);
+ }
+ }
+ if (numErrors > 0)
+ printf("%d triangles differ\n", numErrors);
+ return numErrors == 0;
+}
+
+
+#ifndef LEFT_HANDED_POLYGONS
+static const SkPoint star[] = {
+ // Outer contour is clockwise if Y is down, counterclockwise if Y is up.
+ { SkFloatToScalar(110), SkFloatToScalar( 20) },
+ { SkFloatToScalar(100), SkFloatToScalar( 50) },
+ { SkFloatToScalar(130), SkFloatToScalar( 80) },
+ { SkFloatToScalar( 90), SkFloatToScalar( 80) },
+ { SkFloatToScalar( 70), SkFloatToScalar(120) },
+ { SkFloatToScalar( 50), SkFloatToScalar( 80) },
+ { SkFloatToScalar( 10), SkFloatToScalar( 80) },
+ { SkFloatToScalar( 40), SkFloatToScalar( 50) },
+ { SkFloatToScalar( 30), SkFloatToScalar( 20) },
+ { SkFloatToScalar( 70), SkFloatToScalar( 40) },
+ // Inner contour is counterclockwise if Y is down, clockwise if Y is up.
+ { SkFloatToScalar(110), SkFloatToScalar( 20) },
+ { SkFloatToScalar( 70), SkFloatToScalar( 50) },
+ { SkFloatToScalar( 60), SkFloatToScalar( 60) },
+ { SkFloatToScalar( 60), SkFloatToScalar( 70) },
+ { SkFloatToScalar( 80), SkFloatToScalar( 70) },
+ { SkFloatToScalar( 80), SkFloatToScalar( 60) },
+ { SkFloatToScalar( 70), SkFloatToScalar( 50) }
+};
+static const SkPoint plus[] = {
+ { SkFloatToScalar( 70), SkFloatToScalar( 10) },
+ { SkFloatToScalar( 70), SkFloatToScalar( 50) },
+ { SkFloatToScalar(110), SkFloatToScalar( 50) },
+ { SkFloatToScalar(110), SkFloatToScalar( 70) },
+ { SkFloatToScalar( 70), SkFloatToScalar( 70) },
+ { SkFloatToScalar( 70), SkFloatToScalar(110) },
+ { SkFloatToScalar( 50), SkFloatToScalar(110) },
+ { SkFloatToScalar( 50), SkFloatToScalar( 70) },
+ { SkFloatToScalar( 10), SkFloatToScalar( 70) },
+ { SkFloatToScalar( 10), SkFloatToScalar( 50) },
+ { SkFloatToScalar( 50), SkFloatToScalar( 50) },
+ { SkFloatToScalar( 50), SkFloatToScalar( 10) }
+};
+static const SkPoint zipper[] = {
+ { SkFloatToScalar( 10), SkFloatToScalar( 60) },
+ { SkFloatToScalar( 10), SkFloatToScalar( 10) },
+ { SkFloatToScalar( 20), SkFloatToScalar( 10) },
+ { SkFloatToScalar( 20), SkFloatToScalar( 50) },
+ { SkFloatToScalar( 30), SkFloatToScalar( 50) },
+ { SkFloatToScalar( 30), SkFloatToScalar( 10) },
+ { SkFloatToScalar( 60), SkFloatToScalar( 10) },
+ { SkFloatToScalar( 60), SkFloatToScalar( 50) },
+ { SkFloatToScalar( 70), SkFloatToScalar( 50) },
+ { SkFloatToScalar( 70), SkFloatToScalar( 10) },
+ { SkFloatToScalar(100), SkFloatToScalar( 10) },
+ { SkFloatToScalar(100), SkFloatToScalar( 50) },
+ { SkFloatToScalar(110), SkFloatToScalar( 50) },
+ { SkFloatToScalar(110), SkFloatToScalar( 10) },
+ { SkFloatToScalar(140), SkFloatToScalar( 10) },
+ { SkFloatToScalar(140), SkFloatToScalar( 60) },
+ { SkFloatToScalar(130), SkFloatToScalar( 60) },
+ { SkFloatToScalar(130), SkFloatToScalar( 20) },
+ { SkFloatToScalar(120), SkFloatToScalar( 20) },
+ { SkFloatToScalar(120), SkFloatToScalar( 60) },
+ { SkFloatToScalar( 90), SkFloatToScalar( 60) },
+ { SkFloatToScalar( 90), SkFloatToScalar( 20) },
+ { SkFloatToScalar( 80), SkFloatToScalar( 20) },
+ { SkFloatToScalar( 80), SkFloatToScalar( 60) },
+ { SkFloatToScalar( 50), SkFloatToScalar( 60) },
+ { SkFloatToScalar( 50), SkFloatToScalar( 20) },
+ { SkFloatToScalar( 40), SkFloatToScalar( 20) },
+ { SkFloatToScalar( 40), SkFloatToScalar( 60) }
+};
+#else // LEFT_HANDED_POLYGONS
+static const SkPoint star[] = {
+ // Outer contour is counterclockwise if Y is down, clockwise if Y is up.
+ { SkFloatToScalar(110), SkFloatToScalar( 20) },
+ { SkFloatToScalar( 70), SkFloatToScalar( 40) },
+ { SkFloatToScalar( 30), SkFloatToScalar( 20) },
+ { SkFloatToScalar( 40), SkFloatToScalar( 50) },
+ { SkFloatToScalar( 10), SkFloatToScalar( 80) },
+ { SkFloatToScalar( 50), SkFloatToScalar( 80) },
+ { SkFloatToScalar( 70), SkFloatToScalar(120) },
+ { SkFloatToScalar( 90), SkFloatToScalar( 80) },
+ { SkFloatToScalar(130), SkFloatToScalar( 80) },
+ { SkFloatToScalar(100), SkFloatToScalar( 50) },
+ // Inner contour is clockwise if Y is down, counterclockwise if Y is up.
+ { SkFloatToScalar(110), SkFloatToScalar( 20) },
+ { SkFloatToScalar( 70), SkFloatToScalar( 50) },
+ { SkFloatToScalar( 80), SkFloatToScalar( 60) },
+ { SkFloatToScalar( 80), SkFloatToScalar( 70) },
+ { SkFloatToScalar( 60), SkFloatToScalar( 70) },
+ { SkFloatToScalar( 60), SkFloatToScalar( 60) },
+ { SkFloatToScalar( 70), SkFloatToScalar( 50) }
+};
+#endif // LEFT_HANDED_POLYGONS
+#define kNumStarVertices 10
+#define kNumStarHoleVertices (sizeof(star) / sizeof(star[0]))
+
+
+// Star test
+static void TestStarTriangulation(skiatest::Reporter* reporter) {
+ static const float refTriangles[][3][2] = {
+ { { 30, 20}, { 70, 40}, { 40, 50} }, // { 8, 9, 7 }
+ { {100, 50}, { 10, 80}, { 40, 50} }, // { 1, 6, 7 }
+ { {100, 50}, { 40, 50}, { 70, 40} }, // { 1, 7, 9 }
+ { {100, 50}, { 70, 40}, {110, 20} }, // { 1, 9, 0 }
+ { { 90, 80}, { 70, 120}, { 50, 80} }, // { 3, 4, 5 }
+ { {130, 80}, { 90, 80}, { 50, 80} }, // { 2, 3, 5 } degen
+ { {130, 80}, { 50, 80}, { 10, 80} }, // { 2, 5, 6 } degen
+ { {130, 80}, { 10, 80}, {100, 50} } // { 2, 6, 1 }
+ };
+ const size_t numRefTriangles = sizeof(refTriangles)
+ / (6 * sizeof(refTriangles[0][0][0]));
+ SkTDArray<SkPoint> triangles;
+ bool success = SkConcaveToTriangles(kNumStarVertices, star, &triangles);
+ PrintTriangles(triangles, kNumStarVertices, star, reporter);
+ success = CompareTriangleList(numRefTriangles, refTriangles, triangles)
+ && success;
+ reporter->report("Triangulate Star", success ? reporter->kPassed
+ : reporter->kFailed);
+}
+
+
+// Star with hole test
+static void TestStarHoleTriangulation(skiatest::Reporter* reporter) {
+ static const float refTriangles[][3][2] = {
+ { {100, 50}, { 80, 60}, { 70, 50} }, // { 1, 15, 16 }
+ { {100, 50}, { 70, 50}, {110, 20} }, // { 1, 16, 0 }
+ { { 30, 20}, { 70, 40}, { 40, 50} }, // { 8, 9, 7 }
+ { { 60, 70}, { 80, 70}, { 10, 80} }, // { 13, 14, 6 }
+ { { 60, 60}, { 60, 70}, { 10, 80} }, // { 12, 13, 6 }
+ { { 70, 50}, { 60, 60}, { 10, 80} }, // { 11, 12, 6 }
+ { { 70, 50}, { 10, 80}, { 40, 50} }, // { 11, 6, 7 }
+ { { 70, 50}, { 40, 50}, { 70, 40} }, // { 11, 7, 9 }
+ { { 70, 50}, { 70, 40}, {110, 20} }, // { 11, 9, 10 }
+ { { 90, 80}, { 70, 120}, { 50, 80} }, // { 3, 4, 5 }
+ { {130, 80}, { 90, 80}, { 50, 80} }, // { 2, 3, 5 } degen
+ { {130, 80}, { 50, 80}, { 10, 80} }, // { 2, 5, 6 } degen
+ { {130, 80}, { 10, 80}, { 80, 70} }, // { 2, 6, 14 }
+ { {130, 80}, { 80, 70}, { 80, 60} }, // { 2, 14, 15 }
+ { {130, 80}, { 80, 60}, {100, 50} } // { 2, 15, 1 }
+ };
+ const size_t numRefTriangles = sizeof(refTriangles)
+ / (6 * sizeof(refTriangles[0][0][0]));
+ SkTDArray<SkPoint> triangles;
+ bool success = SkConcaveToTriangles(kNumStarHoleVertices, star, &triangles);
+ PrintTriangles(triangles, kNumStarHoleVertices, star, reporter);
+ success = CompareTriangleList(numRefTriangles, refTriangles, triangles)
+ && success;
+ reporter->report("Triangulate Star With Hole", success ? reporter->kPassed
+ : reporter->kFailed);
+}
+
+
+// Plus test
+static void TestPlusTriangulation(skiatest::Reporter* reporter) {
+ static const float refTriangles[][3][2] = {
+ { { 50, 10}, { 70, 10}, { 50, 50} }, // { 11, 0, 10 }
+ { { 70, 50}, {110, 50}, { 10, 70} }, // { 1, 2, 8 }
+ { { 70, 50}, { 10, 70}, { 10, 50} }, // { 1, 8, 9 }
+ { { 70, 50}, { 10, 50}, { 50, 50} }, // { 1, 9, 10 }
+ { { 70, 50}, { 50, 50}, { 70, 10} }, // { 1, 10, 0 }
+ { { 70, 70}, { 50, 110}, { 50, 70} }, // { 4, 6, 7 }
+ { {110, 70}, { 70, 70}, { 50, 70} }, // { 3, 4, 7 }
+ { {110, 70}, { 50, 70}, { 10, 70} }, // { 3, 7, 8 }
+ { {110, 70}, { 10, 70}, {110, 50} }, // { 3, 8, 2 }
+ { { 70, 110}, { 50, 110}, { 70, 70} }, // { 5, 6, 4 }
+ };
+ const size_t numRefTriangles = sizeof(refTriangles)
+ / (6 * sizeof(refTriangles[0][0][0]));
+ SkTDArray<SkPoint> triangles;
+ const size_t numVertices = sizeof(plus) / sizeof(SkPoint);
+ bool success = SkConcaveToTriangles(numVertices, plus, &triangles);
+ PrintTriangles(triangles, numVertices, plus, reporter);
+ success = CompareTriangleList(numRefTriangles, refTriangles, triangles)
+ && success;
+ reporter->report("Triangulate Plus", success ? reporter->kPassed
+ : reporter->kFailed);
+}
+
+
+// Zipper test
+static void TestZipperTriangulation(skiatest::Reporter* reporter) {
+ static const float refTriangles[][3][2] = {
+ { { 10, 10}, { 20, 10}, { 20, 50} }, // { 1, 2, 3 }
+ { { 20, 50}, { 30, 50}, { 10, 60} }, // { 3, 4, 0 }
+ { { 10, 10}, { 20, 50}, { 10, 60} }, // { 1, 3, 0 }
+ { { 30, 10}, { 60, 10}, { 40, 20} }, // { 5, 6, 26 }
+ { { 30, 10}, { 40, 20}, { 30, 50} }, // { 5, 26, 4 }
+ { { 40, 60}, { 10, 60}, { 30, 50} }, // { 27, 0, 4 }
+ { { 40, 60}, { 30, 50}, { 40, 20} }, // { 27, 4, 26 }
+ { { 60, 50}, { 70, 50}, { 50, 60} }, // { 7, 8, 24 }
+ { { 50, 20}, { 60, 50}, { 50, 60} }, // { 25, 7, 24 }
+ { { 50, 20}, { 40, 20}, { 60, 10} }, // { 25, 26, 6 }
+ { { 60, 50}, { 50, 20}, { 60, 10} }, // { 7, 25, 6 }
+ { { 70, 10}, {100, 10}, { 80, 20} }, // { 9, 10, 22 }
+ { { 70, 10}, { 80, 20}, { 70, 50} }, // { 9, 22, 8 }
+ { { 80, 60}, { 50, 60}, { 70, 50} }, // { 23, 24, 8 }
+ { { 80, 60}, { 70, 50}, { 80, 20} }, // { 23, 8, 22 }
+ { {100, 50}, {110, 50}, { 90, 60} }, // { 11, 12, 20 }
+ { { 90, 20}, {100, 50}, { 90, 60} }, // { 21, 11, 20 }
+ { { 90, 20}, { 80, 20}, {100, 10} }, // { 21, 22, 10 }
+ { {100, 50}, { 90, 20}, {100, 10} }, // { 11, 21, 10 }
+ { {110, 10}, {140, 10}, {120, 20} }, // { 13, 14, 18 }
+ { {110, 10}, {120, 20}, {110, 50} }, // { 13, 18, 12 }
+ { {120, 60}, { 90, 60}, {110, 50} }, // { 19, 20, 12 }
+ { {120, 60}, {110, 50}, {120, 20} }, // { 19, 12, 18 }
+ { {140, 60}, {130, 60}, {130, 20} }, // { 15, 16, 17 }
+ { {130, 20}, {120, 20}, {140, 10} }, // { 17, 18, 14 }
+ { {140, 60}, {130, 20}, {140, 10} }, // { 15, 17, 14 }
+ };
+ const size_t numRefTriangles = sizeof(refTriangles)
+ / (6 * sizeof(refTriangles[0][0][0]));
+ SkTDArray<SkPoint> triangles;
+ const size_t numVertices = sizeof(zipper) / sizeof(SkPoint);
+ bool success = SkConcaveToTriangles(numVertices, zipper, &triangles);
+ PrintTriangles(triangles, numVertices, zipper, reporter);
+ success = CompareTriangleList(numRefTriangles, refTriangles, triangles)
+ && success;
+ reporter->report("Triangulate Zipper", success ? reporter->kPassed
+ : reporter->kFailed);
+}
+
+
+static void TestTriangulation(skiatest::Reporter* reporter) {
+ TestStarTriangulation(reporter);
+ TestStarHoleTriangulation(reporter);
+ TestPlusTriangulation(reporter);
+ TestZipperTriangulation(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("Triangulation", TriangulationTestClass, TestTriangulation)
diff --git a/tests/UtilsTest.cpp b/tests/UtilsTest.cpp
new file mode 100644
index 0000000..8a8319c
--- /dev/null
+++ b/tests/UtilsTest.cpp
@@ -0,0 +1,109 @@
+#include "Test.h"
+#include "SkRandom.h"
+#include "SkTSearch.h"
+#include "SkTSort.h"
+#include "SkUtils.h"
+
+#define kSEARCH_COUNT 91
+
+static void test_search(skiatest::Reporter* reporter) {
+ int i, array[kSEARCH_COUNT];
+ SkRandom rand;
+
+ for (i = 0; i < kSEARCH_COUNT; i++) {
+ array[i] = rand.nextS();
+ }
+
+ SkTHeapSort<int>(array, kSEARCH_COUNT);
+ // make sure we got sorted properly
+ for (i = 1; i < kSEARCH_COUNT; i++) {
+ REPORTER_ASSERT(reporter, array[i-1] <= array[i]);
+ }
+
+ // make sure we can find all of our values
+ for (i = 0; i < kSEARCH_COUNT; i++) {
+ int index = SkTSearch<int>(array, kSEARCH_COUNT, array[i], sizeof(int));
+ REPORTER_ASSERT(reporter, index == i);
+ }
+
+ // make sure that random values are either found, or the correct
+ // insertion index is returned
+ for (i = 0; i < 10000; i++) {
+ int value = rand.nextS();
+ int index = SkTSearch<int>(array, kSEARCH_COUNT, value, sizeof(int));
+
+ if (index >= 0) {
+ REPORTER_ASSERT(reporter,
+ index < kSEARCH_COUNT && array[index] == value);
+ } else {
+ index = ~index;
+ REPORTER_ASSERT(reporter, index <= kSEARCH_COUNT);
+ if (index < kSEARCH_COUNT) {
+ REPORTER_ASSERT(reporter, value < array[index]);
+ if (index > 0) {
+ REPORTER_ASSERT(reporter, value > array[index - 1]);
+ }
+ } else {
+ // we should append the new value
+ REPORTER_ASSERT(reporter, value > array[kSEARCH_COUNT - 1]);
+ }
+ }
+ }
+}
+
+static void test_utf16(skiatest::Reporter* reporter) {
+ static const SkUnichar gUni[] = {
+ 0x10000, 0x18080, 0x20202, 0xFFFFF, 0x101234
+ };
+
+ uint16_t buf[2];
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gUni); i++) {
+ size_t count = SkUTF16_FromUnichar(gUni[i], buf);
+ REPORTER_ASSERT(reporter, count == 2);
+ size_t count2 = SkUTF16_CountUnichars(buf, 2);
+ REPORTER_ASSERT(reporter, count2 == 1);
+ const uint16_t* ptr = buf;
+ SkUnichar c = SkUTF16_NextUnichar(&ptr);
+ REPORTER_ASSERT(reporter, c == gUni[i]);
+ REPORTER_ASSERT(reporter, ptr - buf == 2);
+ }
+}
+
+static void TestUTF(skiatest::Reporter* reporter) {
+ static const struct {
+ const char* fUtf8;
+ SkUnichar fUni;
+ } gTest[] = {
+ { "a", 'a' },
+ { "\x7f", 0x7f },
+ { "\xC2\x80", 0x80 },
+ { "\xC3\x83", (3 << 6) | 3 },
+ { "\xDF\xBF", 0x7ff },
+ { "\xE0\xA0\x80", 0x800 },
+ { "\xE0\xB0\xB8", 0xC38 },
+ { "\xE3\x83\x83", (3 << 12) | (3 << 6) | 3 },
+ { "\xEF\xBF\xBF", 0xFFFF },
+ { "\xF0\x90\x80\x80", 0x10000 },
+ { "\xF3\x83\x83\x83", (3 << 18) | (3 << 12) | (3 << 6) | 3 }
+ };
+
+ for (size_t i = 0; i < SK_ARRAY_COUNT(gTest); i++) {
+ const char* p = gTest[i].fUtf8;
+ int n = SkUTF8_CountUnichars(p);
+ SkUnichar u0 = SkUTF8_ToUnichar(gTest[i].fUtf8);
+ SkUnichar u1 = SkUTF8_NextUnichar(&p);
+
+ REPORTER_ASSERT(reporter, n == 1);
+ REPORTER_ASSERT(reporter, u0 == u1);
+ REPORTER_ASSERT(reporter, u0 == gTest[i].fUni);
+ REPORTER_ASSERT(reporter,
+ p - gTest[i].fUtf8 == (int)strlen(gTest[i].fUtf8));
+ }
+
+ test_utf16(reporter);
+ test_search(reporter);
+}
+
+#include "TestClassDef.h"
+DEFINE_TESTCLASS("UTF", UtfTestClass, TestUTF)
diff --git a/tests/testmain.cpp b/tests/testmain.cpp
new file mode 100644
index 0000000..2e16de3
--- /dev/null
+++ b/tests/testmain.cpp
@@ -0,0 +1,104 @@
+#include "SkGraphics.h"
+#include "Test.h"
+
+using namespace skiatest;
+
+// need to explicitly declare this, or we get some weird infinite loop llist
+template TestRegistry* TestRegistry::gHead;
+
+class Iter {
+public:
+ Iter(Reporter* r) : fReporter(r) {
+ r->ref();
+ fReg = TestRegistry::Head();
+ }
+
+ ~Iter() {
+ fReporter->unref();
+ }
+
+ Test* next() {
+ if (fReg) {
+ TestRegistry::Factory fact = fReg->factory();
+ fReg = fReg->next();
+ Test* test = fact(NULL);
+ test->setReporter(fReporter);
+ return test;
+ }
+ return NULL;
+ }
+
+ static int Count() {
+ const TestRegistry* reg = TestRegistry::Head();
+ int count = 0;
+ while (reg) {
+ count += 1;
+ reg = reg->next();
+ }
+ return count;
+ }
+
+private:
+ Reporter* fReporter;
+ const TestRegistry* fReg;
+};
+
+static const char* result2string(Reporter::Result result) {
+ return result == Reporter::kPassed ? "passed" : "FAILED";
+}
+
+class DebugfReporter : public Reporter {
+public:
+ void setIndexOfTotal(int index, int total) {
+ fIndex = index;
+ fTotal = total;
+ }
+protected:
+ virtual void onStart(Test* test) {
+ SkDebugf("Running [%d/%d] %s...\n", fIndex+1, fTotal, test->getName());
+ }
+ virtual void onReport(const char desc[], Reporter::Result result) {
+ SkDebugf("\t%s: %s\n", result2string(result), desc);
+ }
+ virtual void onEnd(Test* test) {}
+private:
+ int fIndex, fTotal;
+};
+
+class SkAutoGraphics {
+public:
+ SkAutoGraphics() {
+ SkGraphics::Init();
+ }
+ ~SkAutoGraphics() {
+ SkGraphics::Term();
+ }
+};
+
+int main (int argc, char * const argv[]) {
+ SkAutoGraphics ag;
+
+ DebugfReporter reporter;
+ Iter iter(&reporter);
+ Test* test;
+
+ const int count = Iter::Count();
+ int index = 0;
+ while ((test = iter.next()) != NULL) {
+ reporter.setIndexOfTotal(index, count);
+ test->run();
+ SkDELETE(test);
+ index += 1;
+ }
+ SkDebugf("Finished %d tests.\n", count);
+
+#if 0
+ int total = reporter.countTests();
+ int passed = reporter.countResults(Reporter::kPassed);
+ int failed = reporter.countResults(Reporter::kFailed);
+ SkDebugf("Tests=%d Passed=%d (%g%%) Failed=%d (%g%%)\n", total,
+ passed, passed * 100.f / total,
+ failed, failed * 100.f / total);
+#endif
+ return 0;
+}