1cb93a386Sopenharmony_civar dumpErrors = false; 2cb93a386Sopenharmony_civar container; 3cb93a386Sopenharmony_ci 4cb93a386Sopenharmony_cifunction getViewBox(path) { 5cb93a386Sopenharmony_ci let bounds = path.getBounds(); 6cb93a386Sopenharmony_ci return `${(bounds.fLeft-2)*.95} ${(bounds.fTop-2)*.95} ${(bounds.fRight+2)*1.05} ${(bounds.fBottom+2)*1.05}`; 7cb93a386Sopenharmony_ci} 8cb93a386Sopenharmony_ci 9cb93a386Sopenharmony_cifunction addSVG(testName, expectedPath, actualPath, message) { 10cb93a386Sopenharmony_ci if (!dumpErrors) { 11cb93a386Sopenharmony_ci return; 12cb93a386Sopenharmony_ci } 13cb93a386Sopenharmony_ci if (!container) { 14cb93a386Sopenharmony_ci let styleEl = document.createElement('style'); 15cb93a386Sopenharmony_ci document.head.appendChild(styleEl); 16cb93a386Sopenharmony_ci let sheet = styleEl.sheet; 17cb93a386Sopenharmony_ci sheet.insertRule(`svg { 18cb93a386Sopenharmony_ci border: 1px solid #DDD; 19cb93a386Sopenharmony_ci max-width: 45%; 20cb93a386Sopenharmony_ci vertical-align: top; 21cb93a386Sopenharmony_ci }`, 0); 22cb93a386Sopenharmony_ci 23cb93a386Sopenharmony_ci container = document.createElement('div'); 24cb93a386Sopenharmony_ci document.body.appendChild(container); 25cb93a386Sopenharmony_ci 26cb93a386Sopenharmony_ci } 27cb93a386Sopenharmony_ci 28cb93a386Sopenharmony_ci let thisTest = document.createElement('div'); 29cb93a386Sopenharmony_ci thisTest.innerHTML = ` 30cb93a386Sopenharmony_ci <h2>Failed test ${testName}</h2> 31cb93a386Sopenharmony_ci 32cb93a386Sopenharmony_ci <div>${message}</div> 33cb93a386Sopenharmony_ci 34cb93a386Sopenharmony_ci <svg class='expected' viewBox='${getViewBox(expectedPath)}'> 35cb93a386Sopenharmony_ci <path stroke=black fill=white stroke-width=0.01 d="${expectedPath.toSVGString()}"></path> 36cb93a386Sopenharmony_ci </svg> 37cb93a386Sopenharmony_ci 38cb93a386Sopenharmony_ci <svg class='actual' viewBox='${getViewBox(actualPath)}'> 39cb93a386Sopenharmony_ci <path stroke=black fill=white stroke-width=0.01 d="${actualPath.toSVGString()}"></path> 40cb93a386Sopenharmony_ci </svg> 41cb93a386Sopenharmony_ci`; 42cb93a386Sopenharmony_ci container.appendChild(thisTest); 43cb93a386Sopenharmony_ci 44cb93a386Sopenharmony_ci} 45cb93a386Sopenharmony_ci 46cb93a386Sopenharmony_ciconst TOLERANCE = 0.0001; 47cb93a386Sopenharmony_ci 48cb93a386Sopenharmony_cifunction diffPaths(expected, actual) { 49cb93a386Sopenharmony_ci // Look through commands and see if they are within tolerance. 50cb93a386Sopenharmony_ci let eCmds = expected.toCmds(), aCmds = actual.toCmds(); 51cb93a386Sopenharmony_ci if (eCmds.length !== aCmds.length) { 52cb93a386Sopenharmony_ci //console.log(`Expected: ${JSON.stringify(eCmds)} and Actual: ${JSON.stringify(aCmds)}`); 53cb93a386Sopenharmony_ci return `Different amount of verbs. Expected had ${eCmds.length}, Actual had ${aCmds.length}`; 54cb93a386Sopenharmony_ci } 55cb93a386Sopenharmony_ci for(let idx = 0; idx < eCmds.length; idx++){ 56cb93a386Sopenharmony_ci let eCmd = eCmds[idx], aCmd = aCmds[idx]; 57cb93a386Sopenharmony_ci if (eCmd.length !== aCmd.length) { 58cb93a386Sopenharmony_ci // Should never happen, means WASM code is returning bad ops. 59cb93a386Sopenharmony_ci return `Command index ${idx} differs in num arguments. Expected had ${eCmd.length}, Actual had ${aCmd.length}`; 60cb93a386Sopenharmony_ci } 61cb93a386Sopenharmony_ci let eVerb = eCmd[0], aVerb = aCmd[0]; 62cb93a386Sopenharmony_ci if (eVerb !== aVerb) { 63cb93a386Sopenharmony_ci return `Command index ${idx} differs. Expected had ${eVerb}, Actual had ${aVerb}`; 64cb93a386Sopenharmony_ci } 65cb93a386Sopenharmony_ci for (let arg = 1; arg < eCmd.length; arg++) { 66cb93a386Sopenharmony_ci if (Math.abs(eCmd[arg] - aCmd[arg]) > TOLERANCE) { 67cb93a386Sopenharmony_ci return `Command index ${idx} has different argument for verb ${eVerb} at position ${arg}. Expected had ${eCmd[arg]}, Actual had ${aCmd[arg]}` 68cb93a386Sopenharmony_ci } 69cb93a386Sopenharmony_ci } 70cb93a386Sopenharmony_ci } 71cb93a386Sopenharmony_ci return null; 72cb93a386Sopenharmony_ci} 73cb93a386Sopenharmony_ci 74cb93a386Sopenharmony_cidescribe('PathKit\'s PathOps Behavior', function() { 75cb93a386Sopenharmony_ci var PATHOP_MAP = {}; 76cb93a386Sopenharmony_ci var FILLTYPE_MAP = {}; 77cb93a386Sopenharmony_ci 78cb93a386Sopenharmony_ci function init() { 79cb93a386Sopenharmony_ci if (PathKit && !PATHOP_MAP['kIntersect_SkPathOp']) { 80cb93a386Sopenharmony_ci PATHOP_MAP = { 81cb93a386Sopenharmony_ci 'kIntersect_SkPathOp': PathKit.PathOp.INTERSECT, 82cb93a386Sopenharmony_ci 'kDifference_SkPathOp': PathKit.PathOp.DIFFERENCE, 83cb93a386Sopenharmony_ci 'kUnion_SkPathOp': PathKit.PathOp.UNION, 84cb93a386Sopenharmony_ci 'kXOR_SkPathOp': PathKit.PathOp.XOR, 85cb93a386Sopenharmony_ci 'kXOR_PathOp': PathKit.PathOp.XOR, 86cb93a386Sopenharmony_ci 'kReverseDifference_SkPathOp': PathKit.PathOp.REVERSE_DIFFERENCE, 87cb93a386Sopenharmony_ci }; 88cb93a386Sopenharmony_ci FILLTYPE_MAP = { 89cb93a386Sopenharmony_ci 'kWinding_FillType': PathKit.FillType.WINDING, 90cb93a386Sopenharmony_ci 'kEvenOdd_FillType': PathKit.FillType.EVENODD, 91cb93a386Sopenharmony_ci 'kInverseWinding_FillType': PathKit.FillType.INVERSE_WINDING, 92cb93a386Sopenharmony_ci 'kInverseEvenOdd_FillType': PathKit.FillType.INVERSE_EVENODD, 93cb93a386Sopenharmony_ci }; 94cb93a386Sopenharmony_ci } 95cb93a386Sopenharmony_ci } 96cb93a386Sopenharmony_ci 97cb93a386Sopenharmony_ci function getFillType(str) { 98cb93a386Sopenharmony_ci let e = FILLTYPE_MAP[str]; 99cb93a386Sopenharmony_ci expect(e).toBeTruthy(`Could not find FillType Enum for ${str}`); 100cb93a386Sopenharmony_ci return e; 101cb93a386Sopenharmony_ci } 102cb93a386Sopenharmony_ci 103cb93a386Sopenharmony_ci function getPathOp(str) { 104cb93a386Sopenharmony_ci let e = PATHOP_MAP[str]; 105cb93a386Sopenharmony_ci expect(e).toBeTruthy(`Could not find PathOp Enum for ${str}`); 106cb93a386Sopenharmony_ci return e; 107cb93a386Sopenharmony_ci } 108cb93a386Sopenharmony_ci 109cb93a386Sopenharmony_ci it('combines two paths with .op() and matches what we see from C++', function(done) { 110cb93a386Sopenharmony_ci LoadPathKit.then(catchException(done, () => { 111cb93a386Sopenharmony_ci init(); 112cb93a386Sopenharmony_ci // Test JSON created with: 113cb93a386Sopenharmony_ci // ./out/Clang/pathops_unittest -J ./modules/pathkit/tests/PathOpsOp.json -m PathOpsOp$ 114cb93a386Sopenharmony_ci fetch('/base/tests/PathOpsOp.json').then((r) => { 115cb93a386Sopenharmony_ci r.json().then((json) => { 116cb93a386Sopenharmony_ci expect(json).toBeTruthy(); 117cb93a386Sopenharmony_ci let testNames = Object.keys(json); 118cb93a386Sopenharmony_ci // Assert we loaded a non-zero amount of tests, i.e. the JSON is valid. 119cb93a386Sopenharmony_ci expect(testNames.length > 0).toBeTruthy(); 120cb93a386Sopenharmony_ci testNames.sort(); 121cb93a386Sopenharmony_ci for (testName of testNames) { 122cb93a386Sopenharmony_ci let test = json[testName]; 123cb93a386Sopenharmony_ci 124cb93a386Sopenharmony_ci let path1 = PathKit.FromCmds(test.p1); 125cb93a386Sopenharmony_ci expect(path1).not.toBeNull(`path1 error when loading cmds '${test.p1}'`); 126cb93a386Sopenharmony_ci path1.setFillType(getFillType(test.fillType1)); 127cb93a386Sopenharmony_ci 128cb93a386Sopenharmony_ci let path2 = PathKit.FromCmds(test.p2); 129cb93a386Sopenharmony_ci expect(path2).not.toBeNull(`path2 error when loading cmds '${test.p2}'`); 130cb93a386Sopenharmony_ci path2.setFillType(getFillType(test.fillType2)); 131cb93a386Sopenharmony_ci 132cb93a386Sopenharmony_ci let combined = path1.op(path2, getPathOp(test.op)); 133cb93a386Sopenharmony_ci 134cb93a386Sopenharmony_ci if (test.expectSuccess === 'no') { 135cb93a386Sopenharmony_ci expect(combined).toBeNull(`Test ${testName} should have not created output, but did`); 136cb93a386Sopenharmony_ci } else { 137cb93a386Sopenharmony_ci expect(combined).not.toBeNull(); 138cb93a386Sopenharmony_ci let expected = PathKit.FromCmds(test.out); 139cb93a386Sopenharmony_ci // Do a tolerant match. 140cb93a386Sopenharmony_ci let diff = diffPaths(expected, combined); 141cb93a386Sopenharmony_ci if (test.expectMatch === 'yes'){ 142cb93a386Sopenharmony_ci // Check fill type 143cb93a386Sopenharmony_ci expect(combined.getFillType().value).toEqual(getFillType(test.fillTypeOut).value); 144cb93a386Sopenharmony_ci // diff should be null if the paths are identical (modulo rounding) 145cb93a386Sopenharmony_ci if (diff) { 146cb93a386Sopenharmony_ci expect(`[${testName}] ${diff}`).toBe(''); 147cb93a386Sopenharmony_ci addSVG('[PathOps] ' + testName, expected, combined, diff); 148cb93a386Sopenharmony_ci } 149cb93a386Sopenharmony_ci } else if (test.expectMatch === 'flaky') { 150cb93a386Sopenharmony_ci // Don't worry about it, at least it didn't crash. 151cb93a386Sopenharmony_ci } else { 152cb93a386Sopenharmony_ci if (!diff) { 153cb93a386Sopenharmony_ci expect(`[${testName}] was expected to have paths that differed`).not.toBe(''); 154cb93a386Sopenharmony_ci } 155cb93a386Sopenharmony_ci } 156cb93a386Sopenharmony_ci expected.delete(); 157cb93a386Sopenharmony_ci } 158cb93a386Sopenharmony_ci // combined === path1, so we only have to delete one. 159cb93a386Sopenharmony_ci path1.delete(); 160cb93a386Sopenharmony_ci path2.delete(); 161cb93a386Sopenharmony_ci } 162cb93a386Sopenharmony_ci done(); 163cb93a386Sopenharmony_ci }); 164cb93a386Sopenharmony_ci }); 165cb93a386Sopenharmony_ci })); 166cb93a386Sopenharmony_ci }); 167cb93a386Sopenharmony_ci 168cb93a386Sopenharmony_ci it('simplifies a path with .simplify() and matches what we see from C++', function(done) { 169cb93a386Sopenharmony_ci LoadPathKit.then(catchException(done, () => { 170cb93a386Sopenharmony_ci init(); 171cb93a386Sopenharmony_ci // Test JSON created with: 172cb93a386Sopenharmony_ci // ./out/Clang/pathops_unittest -J ./modules/pathkit/tests/PathOpsSimplify.json -m PathOpsSimplify$ 173cb93a386Sopenharmony_ci fetch('/base/tests/PathOpsSimplify.json').then((r) => { 174cb93a386Sopenharmony_ci r.json().then((json) => { 175cb93a386Sopenharmony_ci expect(json).toBeTruthy(); 176cb93a386Sopenharmony_ci let testNames = Object.keys(json); 177cb93a386Sopenharmony_ci // Assert we loaded a non-zero amount of tests, i.e. the JSON is valid. 178cb93a386Sopenharmony_ci expect(testNames.length > 0).toBeTruthy(); 179cb93a386Sopenharmony_ci testNames.sort(); 180cb93a386Sopenharmony_ci for (testName of testNames) { 181cb93a386Sopenharmony_ci let test = json[testName]; 182cb93a386Sopenharmony_ci 183cb93a386Sopenharmony_ci let path = PathKit.FromCmds(test.path); 184cb93a386Sopenharmony_ci expect(path).not.toBeNull(`path1 error when loading cmds '${test.path}'`); 185cb93a386Sopenharmony_ci path.setFillType(getFillType(test.fillType)); 186cb93a386Sopenharmony_ci 187cb93a386Sopenharmony_ci let simplified = path.simplify(); 188cb93a386Sopenharmony_ci 189cb93a386Sopenharmony_ci if (test.expectSuccess === 'no') { 190cb93a386Sopenharmony_ci expect(simplified).toBeNull(`Test ${testName} should have not created output, but did`); 191cb93a386Sopenharmony_ci } else { 192cb93a386Sopenharmony_ci expect(simplified).not.toBeNull(); 193cb93a386Sopenharmony_ci let expected = PathKit.FromCmds(test.out); 194cb93a386Sopenharmony_ci // Do a tolerant match. 195cb93a386Sopenharmony_ci let diff = diffPaths(expected, simplified); 196cb93a386Sopenharmony_ci if (test.expectMatch === 'yes'){ 197cb93a386Sopenharmony_ci // Check fill type 198cb93a386Sopenharmony_ci expect(simplified.getFillType().value).toEqual(getFillType(test.fillTypeOut).value); 199cb93a386Sopenharmony_ci // diff should be null if the paths are identical (modulo rounding) 200cb93a386Sopenharmony_ci if (diff) { 201cb93a386Sopenharmony_ci expect(`[${testName}] ${diff}`).toBe(''); 202cb93a386Sopenharmony_ci addSVG('[Simplify] ' + testName, expected, simplified, diff); 203cb93a386Sopenharmony_ci } 204cb93a386Sopenharmony_ci } else if (test.expectMatch === 'flaky') { 205cb93a386Sopenharmony_ci // Don't worry about it, at least it didn't crash. 206cb93a386Sopenharmony_ci } else { 207cb93a386Sopenharmony_ci if (!diff) { 208cb93a386Sopenharmony_ci expect(`[${testName}] was expected to not match output`).not.toBe(''); 209cb93a386Sopenharmony_ci } 210cb93a386Sopenharmony_ci } 211cb93a386Sopenharmony_ci expected.delete(); 212cb93a386Sopenharmony_ci } 213cb93a386Sopenharmony_ci // simplified === path, so we only have to delete one. 214cb93a386Sopenharmony_ci path.delete(); 215cb93a386Sopenharmony_ci } 216cb93a386Sopenharmony_ci done(); 217cb93a386Sopenharmony_ci }); 218cb93a386Sopenharmony_ci }); 219cb93a386Sopenharmony_ci })); 220cb93a386Sopenharmony_ci }); 221cb93a386Sopenharmony_ci}); 222