shape ops work in progress
milestone: about 1.6M tests pass

git-svn-id: http://skia.googlecode.com/svn/trunk@5035 2bbb7eff-a529-9590-31e7-b0007b416f81
diff --git a/experimental/Intersection/EdgeWalker_TestUtility.cpp b/experimental/Intersection/EdgeWalker_TestUtility.cpp
index 7e65e8f..7baa17b 100644
--- a/experimental/Intersection/EdgeWalker_TestUtility.cpp
+++ b/experimental/Intersection/EdgeWalker_TestUtility.cpp
@@ -3,17 +3,38 @@
 #include "SkBitmap.h"
 #include "SkCanvas.h"
 #include "SkPaint.h"
+#include "SkStream.h"
+
 #include <algorithm>
+#include <assert.h>
+#include <errno.h>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/sysctl.h>
 
 #undef SkASSERT
 #define SkASSERT(cond) while (!(cond)) { sk_throw(); }
 
+static const char marker[] =
+    "</div>\n"
+    "\n"
+    "<script type=\"text/javascript\">\n"
+    "\n"
+    "var testDivs = [\n";
+#if 0
+static const char filename[] = "../../experimental/Intersection/debugXX.txt";
+#else
+static const char filename[] = "/flash/debug/XX.txt";
+#endif
+
 static bool gShowPath = false;
 static bool gComparePaths = true;
 //static bool gDrawLastAsciiPaths = true;
 static bool gDrawAllAsciiPaths = false;
 static bool gShowAsciiPaths = false;
-static bool gComparePathsAssert = true;
+static bool gComparePathsAssert = false;
+static bool gPathStrAssert = true;
 
 void showPath(const SkPath& path, const char* str) {
     SkDebugf("%s\n", !str ? "original:" : str);
@@ -196,16 +217,17 @@
         SkScalar yScale = std::max(24.0f / larger.height(), 1.0f);
         errors = scaledDrawTheSame(one, two, xScale, yScale, false, bitmap, canvas);
         if (errors > 5) {
+            SkDebugf("\n");
             scaledDrawTheSame(one, two, xScale, yScale, true, bitmap, canvas);
         }
     }
-    if (errors > max) {
-        SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors); 
+    const int MAX_ERRORS = 20;
+    if (errors > max && errors <= MAX_ERRORS) {
+        SkDebugf("%s errors=%d\n", __FUNCTION__, errors); 
         max = errors;
     }
-    const int MAX_ERRORS = 20;
-    if (errors > MAX_ERRORS) SkDebugf("\n%s errors=%d\n", __FUNCTION__, errors); 
     if (errors > MAX_ERRORS && gComparePathsAssert) {
+        SkDebugf("%s errors=%d\n", __FUNCTION__, errors); 
         showPath(one);
         showPath(two, "simplified:");
         SkASSERT(0);
@@ -256,8 +278,8 @@
     return comparePaths(path, out, bitmap, canvas) == 0;
 }
 
-bool testSimplifyx(const SkPath& path, SkPath& out, SkBitmap& bitmap,
-        SkCanvas* canvas) {
+bool testSimplifyx(const SkPath& path, SkPath& out, State4& state,
+        const char* pathStr) {
     if (gShowPath) {
         showPath(path);
     }
@@ -265,7 +287,16 @@
     if (!gComparePaths) {
         return true;
     }
-    return comparePaths(path, out, bitmap, canvas) == 0;
+    int result = comparePaths(path, out, state.bitmap, state.canvas);
+    if (result && gPathStrAssert) {
+        char temp[8192];
+        bzero(temp, sizeof(temp));
+        SkMemoryWStream stream(temp, sizeof(temp));
+        outputToStream(state, pathStr, stream);
+        SkDebugf(temp);
+        SkASSERT(0);
+    }
+    return result == 0;
 }
 
 bool testSimplifyx(const SkPath& path) {
@@ -281,22 +312,232 @@
     return comparePaths(path, out, bitmap, 0) == 0;
 }
 
+const int maxThreadsAllocated = 64;
+static int maxThreads = 1;
+static int threadIndex;
+State4 threadState[maxThreadsAllocated];
+static int testNumber;
+static const char* testName;
+static bool debugThreads = false;
+
+State4* State4::queue = NULL;
+pthread_mutex_t State4::addQueue = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t State4::checkQueue = PTHREAD_COND_INITIALIZER;
+
 State4::State4() {
     bitmap.setConfig(SkBitmap::kARGB_8888_Config, 150 * 2, 100);
     bitmap.allocPixels();
     canvas = new SkCanvas(bitmap);
 }
 
-void createThread(State4* statePtr, void* (*test)(void* )) {
-    int threadError = pthread_create(&statePtr->threadID, NULL, test,
+void createThread(State4* statePtr, void* (*testFun)(void* )) {
+    int threadError = pthread_create(&statePtr->threadID, NULL, testFun,
             (void*) statePtr);
     SkASSERT(!threadError);
 }
 
-void waitForCompletion(State4 threadState[], int& threadIndex) {
-    for (int index = 0; index < threadIndex; ++index) {
-        pthread_join(threadState[index].threadID, NULL);
+int dispatchTest4(void* (*testFun)(void* ), int a, int b, int c, int d) {
+    int testsRun = 0;
+    
+    if (!gRunTestsInOneThread) {
+        State4* statePtr;
+        pthread_mutex_lock(&State4::addQueue);
+        if (threadIndex < maxThreads) {
+            statePtr = &threadState[threadIndex];
+            statePtr->testsRun = 0;
+            statePtr->a = a;
+            statePtr->b = b;
+            statePtr->c = c;
+            statePtr->d = d;
+            statePtr->done = false;
+            statePtr->index = threadIndex;
+            statePtr->last = false;
+            if (debugThreads) SkDebugf("%s %d create done=%d last=%d\n", __FUNCTION__,
+                    statePtr->index, statePtr->done, statePtr->last);
+            pthread_cond_init(&statePtr->initialized, NULL);
+            ++threadIndex;
+            createThread(statePtr, testFun);
+        } else {
+            while (!State4::queue) {
+                if (debugThreads) SkDebugf("%s checkQueue\n", __FUNCTION__);
+                pthread_cond_wait(&State4::checkQueue, &State4::addQueue);
+            }
+            statePtr = State4::queue;
+            testsRun += statePtr->testsRun;
+            statePtr->testsRun = 0;
+            statePtr->a = a;
+            statePtr->b = b;
+            statePtr->c = c;
+            statePtr->d = d;
+            statePtr->done = false;
+            State4::queue = NULL;
+            for (int index = 0; index < maxThreads; ++index) {
+                if (threadState[index].done) {
+                    State4::queue = &threadState[index];
+                }
+            }
+            if (debugThreads) SkDebugf("%s %d init done=%d last=%d queued=%d\n", __FUNCTION__,
+                    statePtr->index, statePtr->done, statePtr->last,
+                    State4::queue ? State4::queue->index : -1);
+            pthread_cond_signal(&statePtr->initialized);
+        }
+        pthread_mutex_unlock(&State4::addQueue);
+    } else {
+        State4 state;
+        state.a = a;
+        state.b = b;
+        state.c = c;
+        state.d = d;
+        (*testFun)(&state);
+        testsRun++;
     }
-    SkDebugf(".");
+    return testsRun;
+}
+
+void initializeTests(const char* test, size_t testNameSize) {
+    testName = test;
+    if (!gRunTestsInOneThread) {
+        int threads = -1;
+        size_t size = sizeof(threads);
+        sysctlbyname("hw.logicalcpu_max", &threads, &size, NULL, 0);
+        if (threads > 0) {
+            maxThreads = threads;
+        } else {
+            maxThreads = 8;
+        }
+    }
+    if (!gRunTestsInOneThread) {
+        SkFILEStream inFile("../../experimental/Intersection/op.htm");
+        if (inFile.isValid()) {
+            SkTDArray<char> inData;
+            inData.setCount(inFile.getLength());
+            size_t inLen = inData.count();
+            inFile.read(inData.begin(), inLen);
+            inFile.setPath(NULL);
+            char* insert = strstr(inData.begin(), marker);   
+            if (insert) {
+                insert += sizeof(marker) - 1;
+                const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1;
+                testNumber = atoi(numLoc) + 1;
+            }
+        }
+    }
+    for (int index = 0; index < maxThreads; ++index) {
+        State4* statePtr = &threadState[index];
+        strcpy(statePtr->filename, filename);
+        SkASSERT(statePtr->filename[sizeof(filename) - 7] == 'X');
+        SkASSERT(statePtr->filename[sizeof(filename) - 6] == 'X');
+        statePtr->filename[sizeof(filename) - 7] = '0' + index / 10;
+        statePtr->filename[sizeof(filename) - 6] = '0' + index % 10;
+    }
     threadIndex = 0;
 }
+
+void outputProgress(const State4& state, const char* pathStr) {
+    if (gRunTestsInOneThread) {
+        SkDebugf("%s\n", pathStr);
+    } else {
+        SkFILEWStream outFile(state.filename);
+        if (!outFile.isValid()) {
+            SkASSERT(0);
+            return;
+        }
+        outputToStream(state, pathStr, outFile);
+    }
+}
+
+void outputToStream(const State4& state, const char* pathStr, SkWStream& outFile) {
+    outFile.writeText("<div id=\"");
+    outFile.writeText(testName);
+    outFile.writeDecAsText(testNumber);
+    outFile.writeText("\">\n");
+    outFile.writeText(pathStr);
+    outFile.writeText("</div>\n\n");
+    
+    outFile.writeText(marker);
+    outFile.writeText("    ");
+    outFile.writeText(testName);
+    outFile.writeDecAsText(testNumber);
+    outFile.writeText(",\n\n\n");
+    
+    outFile.writeText("static void ");
+    outFile.writeText(testName);
+    outFile.writeDecAsText(testNumber);
+    outFile.writeText("() {\n    SkPath path;\n");
+    outFile.writeText(pathStr);
+    outFile.writeText("    testSimplifyx(path);\n}\n\n");
+    outFile.writeText("static void (*firstTest)() = ");
+    outFile.writeText(testName);
+    outFile.writeDecAsText(testNumber);
+    outFile.writeText(";\n\n");
+
+    outFile.writeText("static struct {\n");
+    outFile.writeText("    void (*fun)();\n");
+    outFile.writeText("    const char* str;\n");
+    outFile.writeText("} tests[] = {\n");
+    outFile.writeText("    TEST(");
+    outFile.writeText(testName);
+    outFile.writeDecAsText(testNumber);
+    outFile.writeText("),\n");
+    outFile.flush();
+}
+
+bool runNextTestSet(State4& state) {
+    if (gRunTestsInOneThread) {
+        return false;
+    }
+    pthread_mutex_lock(&State4::addQueue);
+    state.done = true;
+    State4::queue = &state;
+    if (debugThreads) SkDebugf("%s %d checkQueue done=%d last=%d\n", __FUNCTION__, state.index,
+        state.done, state.last);
+    pthread_cond_signal(&State4::checkQueue);
+    while (state.done && !state.last) {
+        if (debugThreads) SkDebugf("%s %d done=%d last=%d\n", __FUNCTION__, state.index, state.done, state.last);
+        pthread_cond_wait(&state.initialized, &State4::addQueue);
+    }
+    pthread_mutex_unlock(&State4::addQueue);
+    return !state.last;
+}
+
+int waitForCompletion() {
+    int testsRun = 0;
+    if (!gRunTestsInOneThread) {
+        pthread_mutex_lock(&State4::addQueue);
+        int runningThreads = maxThreads;
+        int index;
+        while (runningThreads > 0) {
+            while (!State4::queue) {
+                if (debugThreads) SkDebugf("%s checkQueue\n", __FUNCTION__);
+                pthread_cond_wait(&State4::checkQueue, &State4::addQueue);
+            }
+            while (State4::queue) {
+                --runningThreads;
+                SkDebugf("•");
+                State4::queue->last = true;
+                State4* next;
+                for (index = 0; index < maxThreads; ++index) {
+                    State4& test = threadState[index];
+                    if (test.done && !test.last) {
+                        next = &test;
+                    }
+                }
+                if (debugThreads) SkDebugf("%s %d next=%d deQueue\n", __FUNCTION__,
+                    State4::queue->index, next ? next->index : -1);
+                pthread_cond_signal(&State4::queue->initialized);
+                State4::queue = next;
+            }
+        }
+        pthread_mutex_unlock(&State4::addQueue);
+        for (index = 0; index < maxThreads; ++index) {
+            pthread_join(threadState[index].threadID, NULL);
+            testsRun += threadState[index].testsRun;
+        }
+        SkDebugf("\n");
+    }
+#ifdef SK_DEBUG
+    gDebugMaxWindSum = SK_MaxS32;
+    gDebugMaxWindValue = SK_MaxS32;
+#endif
+    return testsRun;
+}