blob: 720bd8c40304292a0b81d7daddfd0c127cf221bd [file] [log] [blame]
Kevin Lubickd2efe522019-01-04 15:59:06 -05001jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000;
Kevin Lubick644d8e72018-08-09 13:58:04 -04002
3var dumpErrors = false;
4var container;
5
6function getViewBox(path) {
7 let bounds = path.getBounds();
8 return `${(bounds.fLeft-2)*.95} ${(bounds.fTop-2)*.95} ${(bounds.fRight+2)*1.05} ${(bounds.fBottom+2)*1.05}`;
9}
10
11function addSVG(testName, expectedPath, actualPath, message) {
12 if (!dumpErrors) {
13 return;
14 }
15 if (!container) {
16 let styleEl = document.createElement('style');
17 document.head.appendChild(styleEl);
18 let sheet = styleEl.sheet;
19 sheet.insertRule(`svg {
20 border: 1px solid #DDD;
21 max-width: 45%;
22 vertical-align: top;
23 }`, 0);
24
25 container = document.createElement('div');
26 document.body.appendChild(container);
27
28 }
29
30 let thisTest = document.createElement('div');
31 thisTest.innerHTML = `
32 <h2>Failed test ${testName}</h2>
33
34 <div>${message}</div>
35
36 <svg class='expected' viewBox='${getViewBox(expectedPath)}'>
Kevin Lubickf14a3c02018-08-22 09:35:32 -040037 <path stroke=black fill=white stroke-width=0.01 d="${expectedPath.toSVGString()}"></path>
Kevin Lubick644d8e72018-08-09 13:58:04 -040038 </svg>
39
40 <svg class='actual' viewBox='${getViewBox(actualPath)}'>
Kevin Lubickf14a3c02018-08-22 09:35:32 -040041 <path stroke=black fill=white stroke-width=0.01 d="${actualPath.toSVGString()}"></path>
Kevin Lubick644d8e72018-08-09 13:58:04 -040042 </svg>
43`;
44 container.appendChild(thisTest);
45
46}
47
48const TOLERANCE = 0.0001;
49
50function diffPaths(expected, actual) {
51 // Look through commands and see if they are within tolerance.
52 let eCmds = expected.toCmds(), aCmds = actual.toCmds();
53 if (eCmds.length !== aCmds.length) {
54 //console.log(`Expected: ${JSON.stringify(eCmds)} and Actual: ${JSON.stringify(aCmds)}`);
55 return `Different amount of verbs. Expected had ${eCmds.length}, Actual had ${aCmds.length}`;
56 }
57 for(let idx = 0; idx < eCmds.length; idx++){
58 let eCmd = eCmds[idx], aCmd = aCmds[idx];
59 if (eCmd.length !== aCmd.length) {
60 // Should never happen, means WASM code is returning bad ops.
61 return `Command index ${idx} differs in num arguments. Expected had ${eCmd.length}, Actual had ${aCmd.length}`;
62 }
63 let eVerb = eCmd[0], aVerb = aCmd[0];
64 if (eVerb !== aVerb) {
65 return `Command index ${idx} differs. Expected had ${eVerb}, Actual had ${aVerb}`;
66 }
67 for (let arg = 1; arg < eCmd.length; arg++) {
68 if (Math.abs(eCmd[arg] - aCmd[arg]) > TOLERANCE) {
69 return `Command index ${idx} has different argument for verb ${eVerb} at position ${arg}. Expected had ${eCmd[arg]}, Actual had ${aCmd[arg]}`
70 }
71 }
72 }
73 return null;
74}
75
76describe('PathKit\'s PathOps Behavior', function() {
77 // Note, don't try to print the PathKit object - it can cause Karma/Jasmine to lock up.
78 var PathKit = null;
79 var PATHOP_MAP = {};
80 var FILLTYPE_MAP = {};
Kevin Lubick97d6d982018-08-10 15:53:16 -040081 const LoadPathKit = new Promise(function(resolve, reject) {
Kevin Lubick644d8e72018-08-09 13:58:04 -040082 if (PathKit) {
83 resolve();
84 } else {
85 PathKitInit({
Kevin Lubick97d6d982018-08-10 15:53:16 -040086 locateFile: (file) => '/pathkit/'+file,
Kevin Lubick275eaff2019-01-04 14:38:29 -050087 }).ready().then((_PathKit) => {
Kevin Lubick644d8e72018-08-09 13:58:04 -040088 PathKit = _PathKit;
89 PATHOP_MAP = {
Kevin Lubicke71e9ef2018-11-05 07:51:40 -050090 'kIntersect_SkPathOp': PathKit.PathOp.INTERSECT,
91 'kDifference_SkPathOp': PathKit.PathOp.DIFFERENCE,
92 'kUnion_SkPathOp': PathKit.PathOp.UNION,
93 'kXOR_SkPathOp': PathKit.PathOp.XOR,
94 'kXOR_PathOp': PathKit.PathOp.XOR,
Kevin Lubick644d8e72018-08-09 13:58:04 -040095 'kReverseDifference_SkPathOp': PathKit.PathOp.REVERSE_DIFFERENCE,
96 };
97 FILLTYPE_MAP = {
Kevin Lubicke71e9ef2018-11-05 07:51:40 -050098 'kWinding_FillType': PathKit.FillType.WINDING,
99 'kEvenOdd_FillType': PathKit.FillType.EVENODD,
Kevin Lubick644d8e72018-08-09 13:58:04 -0400100 'kInverseWinding_FillType': PathKit.FillType.INVERSE_WINDING,
101 'kInverseEvenOdd_FillType': PathKit.FillType.INVERSE_EVENODD,
102 };
103 resolve();
104 });
105 }
106 });
107
108 function getFillType(str) {
109 let e = FILLTYPE_MAP[str];
110 expect(e).toBeTruthy(`Could not find FillType Enum for ${str}`);
111 return e;
112 }
113
114 function getPathOp(str) {
115 let e = PATHOP_MAP[str];
116 expect(e).toBeTruthy(`Could not find PathOp Enum for ${str}`);
117 return e;
118 }
119
Kevin Lubick644d8e72018-08-09 13:58:04 -0400120 it('combines two paths with .op() and matches what we see from C++', function(done) {
Kevin Lubicke71e9ef2018-11-05 07:51:40 -0500121 LoadPathKit.then(catchException(done, () => {
Kevin Lubick644d8e72018-08-09 13:58:04 -0400122 // Test JSON created with:
Kevin Lubickc7d05712018-08-31 10:03:23 -0400123 // ./out/Clang/pathops_unittest -J ./modules/pathkit/tests/PathOpsOp.json -m PathOpsOp$
Kevin Lubick644d8e72018-08-09 13:58:04 -0400124 fetch('/base/tests/PathOpsOp.json').then((r) => {
Kevin Lubicke71e9ef2018-11-05 07:51:40 -0500125 r.json().then((json) => {
Kevin Lubick644d8e72018-08-09 13:58:04 -0400126 expect(json).toBeTruthy();
127 let testNames = Object.keys(json);
Kevin Lubickd6719ca2018-08-29 08:26:21 -0400128 // Assert we loaded a non-zero amount of tests, i.e. the JSON is valid.
129 expect(testNames.length > 0).toBeTruthy();
Kevin Lubick644d8e72018-08-09 13:58:04 -0400130 testNames.sort();
131 for (testName of testNames) {
132 let test = json[testName];
133
Kevin Lubickd9936482018-08-24 10:44:16 -0400134 let path1 = PathKit.FromCmds(test.p1);
Kevin Lubick644d8e72018-08-09 13:58:04 -0400135 expect(path1).not.toBeNull(`path1 error when loading cmds '${test.p1}'`);
136 path1.setFillType(getFillType(test.fillType1));
137
Kevin Lubickd9936482018-08-24 10:44:16 -0400138 let path2 = PathKit.FromCmds(test.p2);
Kevin Lubick644d8e72018-08-09 13:58:04 -0400139 expect(path2).not.toBeNull(`path2 error when loading cmds '${test.p2}'`);
140 path2.setFillType(getFillType(test.fillType2));
141
142 let combined = path1.op(path2, getPathOp(test.op));
143
144 if (test.expectSuccess === 'no') {
145 expect(combined).toBeNull(`Test ${testName} should have not created output, but did`);
146 } else {
147 expect(combined).not.toBeNull();
Kevin Lubickd9936482018-08-24 10:44:16 -0400148 let expected = PathKit.FromCmds(test.out);
Kevin Lubick644d8e72018-08-09 13:58:04 -0400149 // Do a tolerant match.
150 let diff = diffPaths(expected, combined);
151 if (test.expectMatch === 'yes'){
Kevin Lubick97d6d982018-08-10 15:53:16 -0400152 // Check fill type
153 expect(combined.getFillType().value).toEqual(getFillType(test.fillTypeOut).value);
154 // diff should be null if the paths are identical (modulo rounding)
Kevin Lubick644d8e72018-08-09 13:58:04 -0400155 if (diff) {
156 expect(`[${testName}] ${diff}`).toBe('');
157 addSVG('[PathOps] ' + testName, expected, combined, diff);
158 }
159 } else if (test.expectMatch === 'flaky') {
160 // Don't worry about it, at least it didn't crash.
161 } else {
162 if (!diff) {
163 expect(`[${testName}] was expected to have paths that differed`).not.toBe('');
164 }
165 }
166 expected.delete();
167 }
Kevin Lubick11194ab2018-08-17 13:52:56 -0400168 // combined === path1, so we only have to delete one.
Kevin Lubick644d8e72018-08-09 13:58:04 -0400169 path1.delete();
170 path2.delete();
171 }
172 done();
173 });
174 });
Kevin Lubicke71e9ef2018-11-05 07:51:40 -0500175 }));
Kevin Lubick644d8e72018-08-09 13:58:04 -0400176 });
177
178 it('simplifies a path with .simplify() and matches what we see from C++', function(done) {
Kevin Lubicke71e9ef2018-11-05 07:51:40 -0500179 LoadPathKit.then(catchException(done, () => {
Kevin Lubick644d8e72018-08-09 13:58:04 -0400180 // Test JSON created with:
Kevin Lubickc7d05712018-08-31 10:03:23 -0400181 // ./out/Clang/pathops_unittest -J ./modules/pathkit/tests/PathOpsSimplify.json -m PathOpsSimplify$
Kevin Lubick644d8e72018-08-09 13:58:04 -0400182 fetch('/base/tests/PathOpsSimplify.json').then((r) => {
Kevin Lubicke71e9ef2018-11-05 07:51:40 -0500183 r.json().then((json) => {
Kevin Lubick644d8e72018-08-09 13:58:04 -0400184 expect(json).toBeTruthy();
185 let testNames = Object.keys(json);
Kevin Lubickd6719ca2018-08-29 08:26:21 -0400186 // Assert we loaded a non-zero amount of tests, i.e. the JSON is valid.
187 expect(testNames.length > 0).toBeTruthy();
Kevin Lubick644d8e72018-08-09 13:58:04 -0400188 testNames.sort();
189 for (testName of testNames) {
190 let test = json[testName];
191
Kevin Lubickd9936482018-08-24 10:44:16 -0400192 let path = PathKit.FromCmds(test.path);
Kevin Lubick644d8e72018-08-09 13:58:04 -0400193 expect(path).not.toBeNull(`path1 error when loading cmds '${test.path}'`);
194 path.setFillType(getFillType(test.fillType));
195
196 let simplified = path.simplify();
197
198 if (test.expectSuccess === 'no') {
199 expect(simplified).toBeNull(`Test ${testName} should have not created output, but did`);
200 } else {
201 expect(simplified).not.toBeNull();
Kevin Lubickd9936482018-08-24 10:44:16 -0400202 let expected = PathKit.FromCmds(test.out);
Kevin Lubick644d8e72018-08-09 13:58:04 -0400203 // Do a tolerant match.
204 let diff = diffPaths(expected, simplified);
205 if (test.expectMatch === 'yes'){
Kevin Lubick97d6d982018-08-10 15:53:16 -0400206 // Check fill type
207 expect(simplified.getFillType().value).toEqual(getFillType(test.fillTypeOut).value);
208 // diff should be null if the paths are identical (modulo rounding)
Kevin Lubick644d8e72018-08-09 13:58:04 -0400209 if (diff) {
210 expect(`[${testName}] ${diff}`).toBe('');
211 addSVG('[Simplify] ' + testName, expected, simplified, diff);
212 }
213 } else if (test.expectMatch === 'flaky') {
214 // Don't worry about it, at least it didn't crash.
215 } else {
216 if (!diff) {
217 expect(`[${testName}] was expected to not match output`).not.toBe('');
218 }
219 }
220 expected.delete();
221 }
Kevin Lubick11194ab2018-08-17 13:52:56 -0400222 // simplified === path, so we only have to delete one.
Kevin Lubick644d8e72018-08-09 13:58:04 -0400223 path.delete();
224 }
225 done();
226 });
227 });
Kevin Lubicke71e9ef2018-11-05 07:51:40 -0500228 }));
Kevin Lubick644d8e72018-08-09 13:58:04 -0400229 });
Kevin Lubick97d6d982018-08-10 15:53:16 -0400230});