diff options
author | Mike Reed <> | 2009-04-02 11:11:16 -0700 |
---|---|---|
committer | The Android Open Source Project <initial-contribution@android.com> | 2009-04-02 11:11:16 -0700 |
commit | da3b8b285a5e3e6f344461d67e3370b27701756d (patch) | |
tree | e0c45002b54fa93a80fe90200e006433e140ea3e /tests | |
parent | 0e747d6d0a794242bd214fa44a6a179baeadfdf9 (diff) | |
download | external_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.mk | 35 | ||||
-rw-r--r-- | tests/ClipCubicTest.cpp | 144 | ||||
-rw-r--r-- | tests/GeometryTest.cpp | 16 | ||||
-rw-r--r-- | tests/MathTest.cpp | 375 | ||||
-rw-r--r-- | tests/MatrixTest.cpp | 98 | ||||
-rw-r--r-- | tests/PackBitsTest.cpp | 127 | ||||
-rw-r--r-- | tests/PathMeasureTest.cpp | 42 | ||||
-rw-r--r-- | tests/PathTest.cpp | 55 | ||||
-rw-r--r-- | tests/Sk64Test.cpp | 198 | ||||
-rw-r--r-- | tests/SortTest.cpp | 50 | ||||
-rw-r--r-- | tests/SrcOverTest.cpp | 72 | ||||
-rw-r--r-- | tests/StreamTest.cpp | 94 | ||||
-rw-r--r-- | tests/StringTest.cpp | 54 | ||||
-rw-r--r-- | tests/Test.cpp | 60 | ||||
-rw-r--r-- | tests/Test.h | 98 | ||||
-rw-r--r-- | tests/TestClassDef.h | 24 | ||||
-rw-r--r-- | tests/TriangulationTest.cpp | 292 | ||||
-rw-r--r-- | tests/UtilsTest.cpp | 109 | ||||
-rw-r--r-- | tests/testmain.cpp | 104 |
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; +} |