blob: f1748cdb4ced507f2e68b00d90061b07ad9ce685 [file] [log] [blame]
caryclark@google.com818b0cc2013-04-08 11:50:46 +00001/*
2 * Copyright 2012 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
8#include "PathOpsExtendedTest.h"
9#include "SkBitmap.h"
10#include "SkCanvas.h"
11#include "SkMatrix.h"
12#include "SkPaint.h"
13#include "SkStream.h"
14
15#ifdef SK_BUILD_FOR_MAC
16#include <sys/sysctl.h>
17#endif
18
19bool gShowTestProgress = false;
20bool gAllowExtendedTest = false;
21
22static const char marker[] =
23 "</div>\n"
24 "\n"
25 "<script type=\"text/javascript\">\n"
26 "\n"
27 "var testDivs = [\n";
28
29static const char* opStrs[] = {
30 "kDifference_PathOp",
31 "kIntersect_PathOp",
32 "kUnion_PathOp",
33 "kXor_PathOp",
34};
35
36static const char* opSuffixes[] = {
37 "d",
38 "i",
39 "u",
40 "x",
41};
42
43static bool gShowPath = false;
44static bool gComparePaths = true;
45static bool gShowOutputProgress = false;
46static bool gComparePathsAssert = true;
47static bool gPathStrAssert = true;
48static bool gUsePhysicalFiles = false;
49
50#if FORCE_RELEASE && !defined SK_BUILD_FOR_WIN
51static bool gRunTestsInOneThread = false;
52#else
53static bool gRunTestsInOneThread = true;
54#endif
55
56static void showPathContour(SkPath::Iter& iter) {
57 uint8_t verb;
58 SkPoint pts[4];
59 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
60 switch (verb) {
61 case SkPath::kMove_Verb:
62 SkDebugf("path.moveTo(%1.9g,%1.9g);\n", pts[0].fX, pts[0].fY);
63 continue;
64 case SkPath::kLine_Verb:
65 SkDebugf("path.lineTo(%1.9g,%1.9g);\n", pts[1].fX, pts[1].fY);
66 break;
67 case SkPath::kQuad_Verb:
68 SkDebugf("path.quadTo(%1.9g,%1.9g, %1.9g,%1.9g);\n",
69 pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
70 break;
71 case SkPath::kCubic_Verb:
72 SkDebugf("path.cubicTo(%1.9g,%1.9g, %1.9g,%1.9g, %1.9g,%1.9g);\n",
73 pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, pts[3].fX, pts[3].fY);
74 break;
75 case SkPath::kClose_Verb:
76 SkDebugf("path.close();\n");
77 break;
78 default:
79 SkDEBUGFAIL("bad verb");
80 return;
81 }
82 }
83}
84
85void showPath(const SkPath& path, const char* str) {
86 SkDebugf("%s\n", !str ? "original:" : str);
87 showPath(path);
88}
89
90void showPath(const SkPath& path) {
91 SkPath::Iter iter(path, true);
92#define SUPPORT_RECT_CONTOUR_DETECTION 0
93#if SUPPORT_RECT_CONTOUR_DETECTION
94 int rectCount = path.isRectContours() ? path.rectContours(NULL, NULL) : 0;
95 if (rectCount > 0) {
96 SkTDArray<SkRect> rects;
97 SkTDArray<SkPath::Direction> directions;
98 rects.setCount(rectCount);
99 directions.setCount(rectCount);
100 path.rectContours(rects.begin(), directions.begin());
101 for (int contour = 0; contour < rectCount; ++contour) {
102 const SkRect& rect = rects[contour];
103 SkDebugf("path.addRect(%1.9g, %1.9g, %1.9g, %1.9g, %s);\n", rect.fLeft, rect.fTop,
104 rect.fRight, rect.fBottom, directions[contour] == SkPath::kCCW_Direction
105 ? "SkPath::kCCW_Direction" : "SkPath::kCW_Direction");
106 }
107 return;
108 }
109#endif
110 iter.setPath(path, true);
111 showPathContour(iter);
112}
113
114void showPathData(const SkPath& path) {
115 SkPath::Iter iter(path, true);
116 uint8_t verb;
117 SkPoint pts[4];
118 while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
119 switch (verb) {
120 case SkPath::kMove_Verb:
121 continue;
122 case SkPath::kLine_Verb:
123 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}},\n", pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY);
124 break;
125 case SkPath::kQuad_Verb:
126 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
127 pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY);
128 break;
129 case SkPath::kCubic_Verb:
130 SkDebugf("{{%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}, {%1.9g,%1.9g}},\n",
131 pts[0].fX, pts[0].fY, pts[1].fX, pts[1].fY, pts[2].fX, pts[2].fY, pts[3].fX, pts[3].fY);
132 break;
133 case SkPath::kClose_Verb:
134 break;
135 default:
136 SkDEBUGFAIL("bad verb");
137 return;
138 }
139 }
140}
141
142void showOp(const SkPathOp op) {
143 switch (op) {
144 case kDifference_PathOp:
145 SkDebugf("op difference\n");
146 break;
147 case kIntersect_PathOp:
148 SkDebugf("op intersect\n");
149 break;
150 case kUnion_PathOp:
151 SkDebugf("op union\n");
152 break;
153 case kXOR_PathOp:
154 SkDebugf("op xor\n");
155 break;
156 default:
157 SkASSERT(0);
158 }
159}
160
161static void showPath(const SkPath& path, const char* str, const SkMatrix& scale) {
162 SkPath scaled;
163 SkMatrix inverse;
164 bool success = scale.invert(&inverse);
165 if (!success) {
166 SkASSERT(0);
167 }
168 path.transform(inverse, &scaled);
169 showPath(scaled, str);
170}
171
172const int bitWidth = 64;
173const int bitHeight = 64;
174
175static void scaleMatrix(const SkPath& one, const SkPath& two, SkMatrix& scale) {
176 SkRect larger = one.getBounds();
177 larger.join(two.getBounds());
178 SkScalar largerWidth = larger.width();
179 if (largerWidth < 4) {
180 largerWidth = 4;
181 }
182 SkScalar largerHeight = larger.height();
183 if (largerHeight < 4) {
184 largerHeight = 4;
185 }
186 SkScalar hScale = (bitWidth - 2) / largerWidth;
187 SkScalar vScale = (bitHeight - 2) / largerHeight;
188 scale.reset();
189 scale.preScale(hScale, vScale);
190}
191
192static int pathsDrawTheSame(SkBitmap& bits, const SkPath& scaledOne, const SkPath& scaledTwo,
193 int& error2x2) {
194 if (bits.width() == 0) {
195 bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
196 bits.allocPixels();
197 }
198 SkCanvas canvas(bits);
199 canvas.drawColor(SK_ColorWHITE);
200 SkPaint paint;
201 canvas.save();
202 const SkRect& bounds1 = scaledOne.getBounds();
203 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
204 canvas.drawPath(scaledOne, paint);
205 canvas.restore();
206 canvas.save();
207 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
208 canvas.drawPath(scaledTwo, paint);
209 canvas.restore();
210 int errors2 = 0;
211 int errors = 0;
212 for (int y = 0; y < bitHeight - 1; ++y) {
213 uint32_t* addr1 = bits.getAddr32(0, y);
214 uint32_t* addr2 = bits.getAddr32(0, y + 1);
215 uint32_t* addr3 = bits.getAddr32(bitWidth, y);
216 uint32_t* addr4 = bits.getAddr32(bitWidth, y + 1);
217 for (int x = 0; x < bitWidth - 1; ++x) {
218 // count 2x2 blocks
219 bool err = addr1[x] != addr3[x];
220 if (err) {
221 errors2 += addr1[x + 1] != addr3[x + 1]
222 && addr2[x] != addr4[x] && addr2[x + 1] != addr4[x + 1];
223 errors++;
224 }
225 }
226 }
227 if (errors2 >= 6 || errors > 160) {
228 SkDebugf("%s errors2=%d errors=%d\n", __FUNCTION__, errors2, errors);
229 }
230 error2x2 = errors2;
231 return errors;
232}
233
234static int pathsDrawTheSame(const SkPath& one, const SkPath& two, SkBitmap& bits, SkPath& scaledOne,
235 SkPath& scaledTwo, int& error2x2) {
236 SkMatrix scale;
237 scaleMatrix(one, two, scale);
238 one.transform(scale, &scaledOne);
239 two.transform(scale, &scaledTwo);
240 return pathsDrawTheSame(bits, scaledOne, scaledTwo, error2x2);
241}
242
243bool drawAsciiPaths(const SkPath& one, const SkPath& two, bool drawPaths) {
244 if (!drawPaths) {
245 return true;
246 }
247 const SkRect& bounds1 = one.getBounds();
248 const SkRect& bounds2 = two.getBounds();
249 SkRect larger = bounds1;
250 larger.join(bounds2);
251 SkBitmap bits;
252 char out[256];
253 int bitWidth = SkScalarCeil(larger.width()) + 2;
254 if (bitWidth * 2 + 1 >= (int) sizeof(out)) {
255 return false;
256 }
257 int bitHeight = SkScalarCeil(larger.height()) + 2;
258 if (bitHeight >= (int) sizeof(out)) {
259 return false;
260 }
261 bits.setConfig(SkBitmap::kARGB_8888_Config, bitWidth * 2, bitHeight);
262 bits.allocPixels();
263 SkCanvas canvas(bits);
264 canvas.drawColor(SK_ColorWHITE);
265 SkPaint paint;
266 canvas.save();
267 canvas.translate(-bounds1.fLeft + 1, -bounds1.fTop + 1);
268 canvas.drawPath(one, paint);
269 canvas.restore();
270 canvas.save();
271 canvas.translate(-bounds1.fLeft + 1 + bitWidth, -bounds1.fTop + 1);
272 canvas.drawPath(two, paint);
273 canvas.restore();
274 for (int y = 0; y < bitHeight; ++y) {
275 uint32_t* addr1 = bits.getAddr32(0, y);
276 int x;
277 char* outPtr = out;
278 for (x = 0; x < bitWidth; ++x) {
279 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
280 }
281 *outPtr++ = '|';
282 for (x = bitWidth; x < bitWidth * 2; ++x) {
283 *outPtr++ = addr1[x] == (uint32_t) -1 ? '_' : 'x';
284 }
285 *outPtr++ = '\0';
286 SkDebugf("%s\n", out);
287 }
288 return true;
289}
290
291static void showSimplifiedPath(const SkPath& one, const SkPath& two,
292 const SkPath& scaledOne, const SkPath& scaledTwo) {
293 showPath(one, "original:");
294 showPath(two, "simplified:");
295 drawAsciiPaths(scaledOne, scaledTwo, true);
296}
297
298static int comparePaths(skiatest::Reporter* reporter, const SkPath& one, const SkPath& two,
299 SkBitmap& bitmap) {
300 int errors2x2;
301 SkPath scaledOne, scaledTwo;
302 int errors = pathsDrawTheSame(one, two, bitmap, scaledOne, scaledTwo, errors2x2);
303 if (errors2x2 == 0) {
304 return 0;
305 }
306 const int MAX_ERRORS = 9;
307 if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
308 showSimplifiedPath(one, two, scaledOne, scaledTwo);
309 }
310 if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
311 SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
312 showSimplifiedPath(one, two, scaledOne, scaledTwo);
313 REPORTER_ASSERT(reporter, 0);
314 }
315 return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
316}
317
318static void showPathOpPath(const SkPath& one, const SkPath& two, const SkPath& a, const SkPath& b,
319 const SkPath& scaledOne, const SkPath& scaledTwo, const SkPathOp shapeOp,
320 const SkMatrix& scale) {
321 SkASSERT((unsigned) shapeOp < sizeof(opStrs) / sizeof(opStrs[0]));
322 showPath(a, "minuend:");
323 SkDebugf("op: %s\n", opStrs[shapeOp]);
324 showPath(b, "subtrahend:");
325 // the region often isn't very helpful since it approximates curves with a lot of line-tos
326 if (0) showPath(scaledOne, "region:", scale);
327 showPath(two, "op result:");
328 drawAsciiPaths(scaledOne, scaledTwo, true);
329}
330
331static int comparePaths(skiatest::Reporter* reporter, const SkPath& one, const SkPath& scaledOne,
332 const SkPath& two, const SkPath& scaledTwo, SkBitmap& bitmap,
333 const SkPath& a, const SkPath& b, const SkPathOp shapeOp,
334 const SkMatrix& scale) {
335 int errors2x2;
336 int errors = pathsDrawTheSame(bitmap, scaledOne, scaledTwo, errors2x2);
337 if (errors2x2 == 0) {
338 return 0;
339 }
340 const int MAX_ERRORS = 8;
341 if (errors2x2 == MAX_ERRORS || errors2x2 == MAX_ERRORS - 1) {
342 showPathOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
343 }
344 if (errors2x2 > MAX_ERRORS && gComparePathsAssert) {
345 SkDebugf("%s errors=%d\n", __FUNCTION__, errors);
346 showPathOpPath(one, two, a, b, scaledOne, scaledTwo, shapeOp, scale);
347 REPORTER_ASSERT(reporter, 0);
348 }
349 return errors2x2 > MAX_ERRORS ? errors2x2 : 0;
350}
351
352bool testSimplify(SkPath& path, bool useXor, SkPath& out, State4& state, const char* pathStr) {
353 SkPath::FillType fillType = useXor ? SkPath::kEvenOdd_FillType : SkPath::kWinding_FillType;
354 path.setFillType(fillType);
355 if (gShowPath) {
356 showPath(path);
357 }
358 Simplify(path, &out);
359 if (!gComparePaths) {
360 return true;
361 }
362 int result = comparePaths(state.reporter, path, out, state.bitmap);
363 if (result && gPathStrAssert) {
364 SkDebugf("addTest %s\n", state.filename);
365 char temp[8192];
366 sk_bzero(temp, sizeof(temp));
367 SkMemoryWStream stream(temp, sizeof(temp));
368 const char* pathPrefix = NULL;
369 const char* nameSuffix = NULL;
370 if (fillType == SkPath::kEvenOdd_FillType) {
371 pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n";
372 nameSuffix = "x";
373 }
374 const char testFunction[] = "testSimplifyx(path);";
375 outputToStream(state, pathStr, pathPrefix, nameSuffix, testFunction, stream);
376 SkDebugf(temp);
377 REPORTER_ASSERT(state.reporter, 0);
378 }
379 return result == 0;
380}
381
382bool testSimplify(skiatest::Reporter* reporter, const SkPath& path) {
383 SkPath out;
384 Simplify(path, &out);
385 SkBitmap bitmap;
386 int result = comparePaths(reporter, path, out, bitmap);
387 if (result && gPathStrAssert) {
388 REPORTER_ASSERT(reporter, 0);
389 }
390 return result == 0;
391}
392
393bool testPathOp(skiatest::Reporter* reporter, const SkPath& a, const SkPath& b,
394 const SkPathOp shapeOp) {
395#if FORCE_RELEASE == 0
396 showPathData(a);
397 showOp(shapeOp);
398 showPathData(b);
399#endif
400 SkPath out;
401 Op(a, b, shapeOp, &out);
402 SkPath pathOut, scaledPathOut;
403 SkRegion rgnA, rgnB, openClip, rgnOut;
404 openClip.setRect(-16000, -16000, 16000, 16000);
405 rgnA.setPath(a, openClip);
406 rgnB.setPath(b, openClip);
407 rgnOut.op(rgnA, rgnB, (SkRegion::Op) shapeOp);
408 rgnOut.getBoundaryPath(&pathOut);
409
410 SkMatrix scale;
411 scaleMatrix(a, b, scale);
412 SkRegion scaledRgnA, scaledRgnB, scaledRgnOut;
413 SkPath scaledA, scaledB;
414 scaledA.addPath(a, scale);
415 scaledA.setFillType(a.getFillType());
416 scaledB.addPath(b, scale);
417 scaledB.setFillType(b.getFillType());
418 scaledRgnA.setPath(scaledA, openClip);
419 scaledRgnB.setPath(scaledB, openClip);
420 scaledRgnOut.op(scaledRgnA, scaledRgnB, (SkRegion::Op) shapeOp);
421 scaledRgnOut.getBoundaryPath(&scaledPathOut);
422 SkBitmap bitmap;
423 SkPath scaledOut;
424 scaledOut.addPath(out, scale);
425 scaledOut.setFillType(out.getFillType());
426 int result = comparePaths(reporter, pathOut, scaledPathOut, out, scaledOut, bitmap, a, b,
427 shapeOp, scale);
428 if (result && gPathStrAssert) {
429 REPORTER_ASSERT(reporter, 0);
430 }
431 return result == 0;
432}
433
434const int maxThreadsAllocated = 64;
435static int maxThreads = 1;
436static int threadIndex;
437State4 threadState[maxThreadsAllocated];
438static int testNumber;
439static const char* testName;
440static bool debugThreads = false;
441
442State4* State4::queue = NULL;
443
444#if HARD_CODE_PTHREAD
445pthread_mutex_t State4::addQueue = PTHREAD_MUTEX_INITIALIZER;
446pthread_cond_t State4::checkQueue = PTHREAD_COND_INITIALIZER;
447#else
448SK_DECLARE_STATIC_MUTEX(gQueueMutex);
449#endif
450
451State4::State4() {
452 bitmap.setConfig(SkBitmap::kARGB_8888_Config, 150 * 2, 100);
453 bitmap.allocPixels();
454}
455
456void createThread(State4* statePtr, ThreadFunction testFun) {
457#if HARD_CODE_PTHREAD
458 SkDEBUGCODE(int threadError =) pthread_create(&statePtr->threadID, NULL, testFun,
459 (void*) statePtr);
460 SkASSERT(!threadError);
461#else
462 statePtr->thread = new SkThread(testFun, (void*) statePtr);
463 statePtr->thread->start();
464#endif
465}
466
467int dispatchTest4(ThreadFunction testFun, int a, int b, int c, int d) {
468 int testsRun = 0;
469 State4* statePtr;
470 if (!gRunTestsInOneThread) {
471#if HARD_CODE_PTHREAD
472 pthread_mutex_lock(&State4::addQueue);
473#else
474 SkAutoMutexAcquire aq(&gQueueMutex);
475#endif
476 if (threadIndex < maxThreads) {
477 statePtr = &threadState[threadIndex];
478 statePtr->testsRun = 0;
479 statePtr->a = a;
480 statePtr->b = b;
481 statePtr->c = c;
482 statePtr->d = d;
483 statePtr->done = false;
484 statePtr->index = threadIndex;
485 statePtr->last = false;
486 if (debugThreads) SkDebugf("%s %d create done=%d last=%d\n", __FUNCTION__,
487 statePtr->index, statePtr->done, statePtr->last);
488#if HARD_CODE_PTHREAD
489 pthread_cond_init(&statePtr->initialized, NULL);
490#else
491 // statePtr->thread contains fData which points to SkThread_PThreadData which
492 // contains PThreadEvent fStarted, all of which is initialized by createThread below
493#endif
494 ++threadIndex;
495 createThread(statePtr, testFun);
496 } else {
497 while (!State4::queue) {
498 if (debugThreads) SkDebugf("%s checkQueue\n", __FUNCTION__);
499#if HARD_CODE_PTHREAD
500 pthread_cond_wait(&State4::checkQueue, &State4::addQueue);
501#else
502 // incomplete
503#endif
504 }
505 statePtr = State4::queue;
506 testsRun += statePtr->testsRun;
507 statePtr->testsRun = 0;
508 statePtr->a = a;
509 statePtr->b = b;
510 statePtr->c = c;
511 statePtr->d = d;
512 statePtr->done = false;
513 State4::queue = NULL;
514 for (int index = 0; index < maxThreads; ++index) {
515 if (threadState[index].done) {
516 State4::queue = &threadState[index];
517 }
518 }
519 if (debugThreads) SkDebugf("%s %d init done=%d last=%d queued=%d\n", __FUNCTION__,
520 statePtr->index, statePtr->done, statePtr->last,
521 State4::queue ? State4::queue->index : -1);
522#if HARD_CODE_PTHREAD
523 pthread_cond_signal(&statePtr->initialized);
524#else
525 // incomplete
526#endif
527 }
528#if HARD_CODE_PTHREAD
529 pthread_mutex_unlock(&State4::addQueue);
530#endif
531 } else {
532 statePtr = &threadState[0];
533 testsRun += statePtr->testsRun;
534 statePtr->testsRun = 0;
535 statePtr->a = a;
536 statePtr->b = b;
537 statePtr->c = c;
538 statePtr->d = d;
539 statePtr->done = false;
540 statePtr->index = threadIndex;
541 statePtr->last = false;
542 (*testFun)(statePtr);
543 }
544 return testsRun;
545}
546
547void initializeTests(skiatest::Reporter* reporter, const char* test, size_t testNameSize) {
548 testName = test;
549 if (!gRunTestsInOneThread) {
550 int threads = -1;
551#ifdef SK_BUILD_FOR_MAC
552 size_t size = sizeof(threads);
553 sysctlbyname("hw.logicalcpu_max", &threads, &size, NULL, 0);
554#endif
555 if (threads > 0) {
556 maxThreads = threads;
557 } else {
558 maxThreads = 8;
559 }
560 }
561 SkFILEStream inFile("../../experimental/Intersection/op.htm");
562 if (inFile.isValid()) {
563 SkTDArray<char> inData;
564 inData.setCount(inFile.getLength());
565 size_t inLen = inData.count();
566 inFile.read(inData.begin(), inLen);
567 inFile.setPath(NULL);
568 char* insert = strstr(inData.begin(), marker);
569 if (insert) {
570 insert += sizeof(marker) - 1;
571 const char* numLoc = insert + 4 /* indent spaces */ + testNameSize - 1;
572 testNumber = atoi(numLoc) + 1;
573 }
574 }
575 const char* filename = "debugXX.txt";
576 for (int index = 0; index < maxThreads; ++index) {
577 State4* statePtr = &threadState[index];
578 statePtr->reporter = reporter;
579 strcpy(statePtr->filename, filename);
580 size_t len = strlen(filename);
581 SkASSERT(statePtr->filename[len - 6] == 'X');
582 SkASSERT(statePtr->filename[len - 5] == 'X');
583 statePtr->filename[len - 6] = '0' + index / 10;
584 statePtr->filename[len - 5] = '0' + index % 10;
585 }
586 threadIndex = 0;
587}
588
589void outputProgress(const State4& state, const char* pathStr, SkPath::FillType pathFillType) {
590 if (gRunTestsInOneThread && gShowOutputProgress) {
591 if (pathFillType == SkPath::kEvenOdd_FillType) {
592 SkDebugf(" path.setFillType(SkPath::kEvenOdd_FillType);\n", pathStr);
593 }
594 SkDebugf("%s\n", pathStr);
595 }
596 const char testFunction[] = "testSimplifyx(path);";
597 const char* pathPrefix = NULL;
598 const char* nameSuffix = NULL;
599 if (pathFillType == SkPath::kEvenOdd_FillType) {
600 pathPrefix = " path.setFillType(SkPath::kEvenOdd_FillType);\n";
601 nameSuffix = "x";
602 }
603 if (gUsePhysicalFiles) {
604 SkFILEWStream outFile(state.filename);
605 if (!outFile.isValid()) {
606 SkASSERT(0);
607 return;
608 }
609 outputToStream(state, pathStr, pathPrefix, nameSuffix, testFunction, outFile);
610 return;
611 }
612 state.ramStream.reset();
613 outputToStream(state, pathStr, pathPrefix, nameSuffix, testFunction, state.ramStream);
614}
615
616void outputProgress(const State4& state, const char* pathStr, SkPathOp op) {
617 SkString testFunc("testPathOp(path, pathB, ");
618 testFunc += opStrs[op];
619 testFunc += ");";
620 const char* testFunction = testFunc.c_str();
621 if (gRunTestsInOneThread && gShowOutputProgress) {
622 SkDebugf("%s\n", pathStr);
623 SkDebugf(" %s\n", testFunction);
624 }
625 const char* nameSuffix = opSuffixes[op];
626 if (gUsePhysicalFiles) {
627 SkFILEWStream outFile(state.filename);
628 if (!outFile.isValid()) {
629 SkASSERT(0);
630 return;
631 }
632 outputToStream(state, pathStr, NULL, nameSuffix, testFunction, outFile);
633 return;
634 }
635 state.ramStream.reset();
636 outputToStream(state, pathStr, NULL, nameSuffix, testFunction, state.ramStream);
637}
638
639static void writeTestName(const char* nameSuffix, SkWStream& outFile) {
640 outFile.writeText(testName);
641 outFile.writeDecAsText(testNumber);
642 if (nameSuffix) {
643 outFile.writeText(nameSuffix);
644 }
645}
646
647void outputToStream(const State4& state, const char* pathStr, const char* pathPrefix,
648 const char* nameSuffix,
649 const char* testFunction, SkWStream& outFile) {
650 outFile.writeText("<div id=\"");
651 writeTestName(nameSuffix, outFile);
652 outFile.writeText("\">\n");
653 if (pathPrefix) {
654 outFile.writeText(pathPrefix);
655 }
656 outFile.writeText(pathStr);
657 outFile.writeText("</div>\n\n");
658
659 outFile.writeText(marker);
660 outFile.writeText(" ");
661 writeTestName(nameSuffix, outFile);
662 outFile.writeText(",\n\n\n");
663
664 outFile.writeText("static void ");
665 writeTestName(nameSuffix, outFile);
666 outFile.writeText("() {\n SkPath path");
667 if (!pathPrefix) {
668 outFile.writeText(", pathB");
669 }
670 outFile.writeText(";\n");
671 if (pathPrefix) {
672 outFile.writeText(pathPrefix);
673 }
674 outFile.writeText(pathStr);
675 outFile.writeText(" ");
676 outFile.writeText(testFunction);
677 outFile.writeText("\n}\n\n");
678 outFile.writeText("static void (*firstTest)() = ");
679 writeTestName(nameSuffix, outFile);
680 outFile.writeText(";\n\n");
681
682 outFile.writeText("static struct {\n");
683 outFile.writeText(" void (*fun)();\n");
684 outFile.writeText(" const char* str;\n");
685 outFile.writeText("} tests[] = {\n");
686 outFile.writeText(" TEST(");
687 writeTestName(nameSuffix, outFile);
688 outFile.writeText("),\n");
689 outFile.flush();
690}
691
692bool runNextTestSet(State4& state) {
693 if (gRunTestsInOneThread) {
694 return false;
695 }
696#if HARD_CODE_PTHREAD
697 pthread_mutex_lock(&State4::addQueue);
698#else
699 SkAutoMutexAcquire aq(&gQueueMutex);
700#endif
701 state.done = true;
702 State4::queue = &state;
703 if (debugThreads) SkDebugf("%s %d checkQueue done=%d last=%d\n", __FUNCTION__, state.index,
704 state.done, state.last);
705#if HARD_CODE_PTHREAD
706 pthread_cond_signal(&State4::checkQueue);
707#else
708 // incomplete
709#endif
710 while (state.done && !state.last) {
711 if (debugThreads) SkDebugf("%s %d done=%d last=%d\n", __FUNCTION__, state.index, state.done, state.last);
712#if HARD_CODE_PTHREAD
713 pthread_cond_wait(&state.initialized, &State4::addQueue);
714#else
715 // incomplete
716#endif
717 }
718#if HARD_CODE_PTHREAD
719 pthread_mutex_unlock(&State4::addQueue);
720#endif
721 return !state.last;
722}
723
724int waitForCompletion() {
725 int testsRun = 0;
726 if (!gRunTestsInOneThread) {
727#if HARD_CODE_PTHREAD
728 pthread_mutex_lock(&State4::addQueue);
729#else
730 SkAutoMutexAcquire aq(gQueueMutex);
731#endif
732 int runningThreads = threadIndex;
733 int index;
734 while (runningThreads > 0) {
735 while (!State4::queue) {
736 if (debugThreads) SkDebugf("%s checkQueue\n", __FUNCTION__);
737#if HARD_CODE_PTHREAD
738 pthread_cond_wait(&State4::checkQueue, &State4::addQueue);
739#else
740 // ioncomplete
741#endif
742 }
743 while (State4::queue) {
744 --runningThreads;
745#if DEBUG_SHOW_TEST_PROGRESS
746 SkDebugf("•");
747#endif
748 State4::queue->last = true;
749 State4* next = NULL;
750 for (index = 0; index < maxThreads; ++index) {
751 State4& test = threadState[index];
752 if (test.done && !test.last) {
753 next = &test;
754 }
755 }
756 if (debugThreads) SkDebugf("%s %d next=%d deQueue\n", __FUNCTION__,
757 State4::queue->index, next ? next->index : -1);
758#if HARD_CODE_PTHREAD
759 pthread_cond_signal(&State4::queue->initialized);
760#else
761 // incomplete
762#endif
763 State4::queue = next;
764 }
765 }
766#if HARD_CODE_PTHREAD
767 pthread_mutex_unlock(&State4::addQueue);
768#endif
769 for (index = 0; index < maxThreads; ++index) {
770#if HARD_CODE_PTHREAD
771 pthread_join(threadState[index].threadID, NULL);
772#else
773 threadState[index].thread->join();
774 delete threadState[index].thread;
775#endif
776 testsRun += threadState[index].testsRun;
777 }
778#if DEBUG_SHOW_TEST_PROGRESS
779 SkDebugf("\n");
780#endif
781 }
782#ifdef SK_DEBUG
783 gDebugMaxWindSum = SK_MaxS32;
784 gDebugMaxWindValue = SK_MaxS32;
785#endif
786 return testsRun;
787}
788
789void RunTestSet(skiatest::Reporter* reporter, TestDesc tests[], size_t count,
790 void (*firstTest)(skiatest::Reporter* ),
791 void (*stopTest)(skiatest::Reporter* ), bool reverse) {
792 size_t index;
793 if (firstTest) {
794 index = count - 1;
795 while (index > 0 && tests[index].fun != firstTest) {
796 --index;
797 }
798#if FORCE_RELEASE == 0
799 SkDebugf("<div id=\"%s\">\n", tests[index].str);
800 SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
801#endif
802 (*tests[index].fun)(reporter);
803 }
804 index = reverse ? count - 1 : 0;
805 size_t last = reverse ? 0 : count - 1;
806 do {
807 if (tests[index].fun != firstTest) {
808 #if FORCE_RELEASE == 0
809 SkDebugf("<div id=\"%s\">\n", tests[index].str);
810 SkDebugf(" %s [%s]\n", __FUNCTION__, tests[index].str);
811 #endif
812 (*tests[index].fun)(reporter);
813 }
814 if (tests[index].fun == stopTest) {
815 SkDebugf("lastTest\n");
816 }
817 if (index == last) {
818 break;
819 }
820 index += reverse ? -1 : 1;
821 } while (true);
822}