Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(839)

Side by Side Diff: third_party/WebKit/LayoutTests/editing/spelling/spellcheck_test.js

Issue 2590823006: Get rid of verify-with-timeout in spellcheck_test (Closed)
Patch Set: Separation of other fixes Created 4 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 'use strict'; 5 'use strict';
6 6
7 // This file provides 7 // This file provides
8 // |spellcheck_test(sample, tester, expectedText, opt_title)| asynchronous test 8 // |spellcheck_test(sample, tester, expectedText, opt_title)| asynchronous test
9 // to W3C test harness for easier writing of spellchecker test cases. 9 // to W3C test harness for easier writing of spellchecker test cases.
10 // 10 //
(...skipping 257 matching lines...) Expand 10 before | Expand all | Expand 10 after
268 */ 268 */
269 serializeInternal(node) { 269 serializeInternal(node) {
270 if (isElement(node)) 270 if (isElement(node))
271 return this.handleElementNode(node); 271 return this.handleElementNode(node);
272 if (isCharacterData(node)) 272 if (isCharacterData(node))
273 return this.handleCharacterData(node); 273 return this.handleCharacterData(node);
274 throw new Error(`Unexpected node ${node}`); 274 throw new Error(`Unexpected node ${node}`);
275 } 275 }
276 } 276 }
277 277
278 /**
279 * @param {!Document} document
280 * @return {boolean}
281 */
282 function hasPendingSpellCheckRequest(document) {
283 return internals.lastSpellCheckRequestSequence(document) !==
284 internals.lastSpellCheckProcessedSequence(document);
285 }
286
278 /** @type {string} */ 287 /** @type {string} */
279 const kTitle = 'title'; 288 const kTitle = 'title';
280 /** @type {string} */ 289 /** @type {string} */
281 const kCallback = 'callback'; 290 const kCallback = 'callback';
282 /** @type {string} */ 291 /** @type {string} */
283 const kIsSpellcheckTest = 'isSpellcheckTest'; 292 const kIsSpellcheckTest = 'isSpellcheckTest';
284 293
285 /**
286 * @param {!Test} testObject
287 * @param {!Sample} sample
288 * @param {string} expectedText
289 * @param {number} remainingRetry
290 * @param {number} retryInterval
291 */
292 function verifyMarkers(
293 testObject, sample, expectedText, remainingRetry, retryInterval) {
294 assert_not_equals(
295 window.internals, undefined,
296 'window.internals is required for running automated spellcheck tests.');
297
298 /** @type {!MarkerSerializer} */
299 const serializer = new MarkerSerializer({
300 spelling: '#',
301 grammar: '~'});
302
303 try {
304 assert_equals(serializer.serialize(sample.document), expectedText);
305 testObject.done();
306 } catch (error) {
307 if (remainingRetry <= 0)
308 throw error;
309
310 // Force invoking idle time spellchecker in case it has not been run yet.
311 if (window.testRunner)
312 window.testRunner.runIdleTasks(() => {});
313
314 // TODO(xiaochengh): We should make SpellCheckRequester::didCheck trigger
315 // something in JavaScript (e.g., a |Promise|), so that we can actively
316 // know the completion of spellchecking instead of passively waiting for
317 // markers to appear or disappear.
318 testObject.step_timeout(
319 () => verifyMarkers(testObject, sample, expectedText,
320 remainingRetry - 1, retryInterval),
321 retryInterval);
322 }
323 }
324
325 // Spellchecker gets triggered not only by text and selection change, but also 294 // Spellchecker gets triggered not only by text and selection change, but also
326 // by focus change. For example, misspelling markers in <INPUT> disappear when 295 // by focus change. For example, misspelling markers in <INPUT> disappear when
327 // the window loses focus, even though the selection does not change. 296 // the window loses focus, even though the selection does not change.
328 // Therefore, we disallow spellcheck tests from running simultaneously to 297 // Therefore, we disallow spellcheck tests from running simultaneously to
329 // prevent interference among them. If we call spellcheck_test while another 298 // prevent interference among them. If we call spellcheck_test while another
330 // test is running, the new test will be added into testQueue waiting for the 299 // test is running, the new test will be added into testQueue waiting for the
331 // completion of the previous test. 300 // completion of the previous test.
332 301
333 /** @type {boolean} */ 302 /** @type {boolean} */
334 var spellcheckTestRunning = false; 303 var spellcheckTestRunning = false;
335 /** @type {!Array<!Object>} */ 304 /** @type {!Array<!Object>} */
336 const testQueue = []; 305 const testQueue = [];
337 306
307 // We need to ensure correct usage of testRunner.runIdleTasks() that:
308 // 1. We don't call runIdleTasks if another runIdleTasks is called but the
309 // callback has not been invoked yet; Otherwise, the current call is ignored.
310 // 2. When the callback is invoked, we only verify test cases whose testers
311 // finished before calling runIdleTasks(); Otherwise, the idle time spell
312 // check may have not been invoked at the verification time.
313
314 /** @type {boolean} */
315 var runIdleTasksRequested = false;
316 /** @type {!Array<!Function>} Verification functions of tests cases whose idle
tkent 2016/12/21 07:42:55 nit: For a multiline annotation, please fold after
Xiaocheng 2016/12/21 07:52:00 Done.
317 * idle spell checkers are requested before the current call of runIdleTasks.
318 */
319 var idleVerificationReadyQueue = [];
320 /** @type {!Array<!Function>} Verification functions of tests cases whose idle
tkent 2016/12/21 07:42:55 Ditto.
Xiaocheng 2016/12/21 07:52:00 Done.
321 * idle spell checkers are requested after the current call of runIdleTasks.
322 */
323 var idleVerificationWaitingQueue = [];
324
325 function batchIdleVerification() {
326 runIdleTasksRequested = false;
327 idleVerificationReadyQueue.forEach(func => func());
328 idleVerificationReadyQueue = idleVerificationWaitingQueue;
329 idleVerificationWaitingQueue = [];
330 if (idleVerificationReadyQueue.length) {
331 runIdleTasksRequested = true;
332 testRunner.runIdleTasks(batchIdleVerification);
333 }
334 }
335
338 /** 336 /**
339 * @param {!Test} testObject 337 * @param {!Test} testObject
340 * @param {!Sample|string} input 338 * @param {!Sample|string} input
341 * @param {function(!Document)|string} tester 339 * @param {function(!Document)|string} tester
342 * @param {string} expectedText 340 * @param {string} expectedText
343 */ 341 */
344 function invokeSpellcheckTest(testObject, input, tester, expectedText) { 342 function invokeSpellcheckTest(testObject, input, tester, expectedText) {
345 spellcheckTestRunning = true; 343 spellcheckTestRunning = true;
346 344
347 testObject.step(() => { 345 testObject.step(() => {
348 // TODO(xiaochengh): Merge the following part with |assert_selection|. 346 // TODO(xiaochengh): Merge the following part with |assert_selection|.
349 /** @type {!Sample} */ 347 /** @type {!Sample} */
350 const sample = typeof(input) === 'string' ? new Sample(input) : input; 348 const sample = typeof(input) === 'string' ? new Sample(input) : input;
351 testObject.sample = sample; 349 testObject.sample = sample;
352 350
353 if (typeof(tester) === 'function') { 351 if (typeof(tester) === 'function') {
354 tester.call(window, sample.document); 352 tester.call(window, sample.document);
355 } else if (typeof(tester) === 'string') { 353 } else if (typeof(tester) === 'string') {
356 const strings = tester.split(/ (.+)/); 354 const strings = tester.split(/ (.+)/);
357 sample.document.execCommand(strings[0], false, strings[1]); 355 sample.document.execCommand(strings[0], false, strings[1]);
358 } else { 356 } else {
359 assert_unreached(`Invalid tester: ${tester}`); 357 assert_unreached(`Invalid tester: ${tester}`);
360 } 358 }
361 359
362 /** @type {number} */ 360 assert_not_equals(
363 const kMaxRetry = 10; 361 window.testRunner, undefined,
364 /** @type {number} */ 362 'window.testRunner is required for automated spellcheck tests.');
365 const kRetryInterval = 50; 363 assert_not_equals(
364 window.internals, undefined,
365 'window.internals is required for automated spellcheck tests.');
366 366
367 // TODO(xiaochengh): We should make SpellCheckRequester::didCheck trigger 367 /** @type {!Function} */
368 // something in JavaScript (e.g., a |Promise|), so that we can actively know 368 const verification = () => {
369 // the completion of spellchecking instead of passively waiting for markers 369 testObject.step(() => {
370 // to appear or disappear. 370 if (hasPendingSpellCheckRequest(sample.document))
371 testObject.step_timeout( 371 return;
372 () => verifyMarkers(testObject, sample, expectedText, 372
373 kMaxRetry, kRetryInterval), 373 /** @type {!MarkerSerializer} */
374 kRetryInterval); 374 const serializer = new MarkerSerializer({
375 spelling: '#',
376 grammar: '~'});
377
378 assert_equals(serializer.serialize(sample.document), expectedText);
379 testObject.done();
380 });
381 }
382
383 // Verify when all spell check requests are resolved.
384 testRunner.setSpellCheckResolvedCallback(verification);
385
386 // For tests that do not expect new markers, verify with runIdleTasks.
387 if (runIdleTasksRequested) {
388 idleVerificationWaitingQueue.push(verification);
389 return;
390 }
391
392 idleVerificationReadyQueue.push(verification);
393 runIdleTasksRequested = true;
394 testRunner.runIdleTasks(batchIdleVerification);
375 }); 395 });
376 } 396 }
377 397
378 add_result_callback(testObj => { 398 add_result_callback(testObj => {
379 if (!testObj.properties[kIsSpellcheckTest]) 399 if (!testObj.properties[kIsSpellcheckTest])
380 return; 400 return;
381 401
382 /** @type {boolean} */ 402 /** @type {boolean} */
383 var shouldRemoveSample = false; 403 var shouldRemoveSample = false;
384 if (testObj.status === testObj.PASS) { 404 if (testObj.status === testObj.PASS) {
385 if (testObj.properties[kCallback]) 405 if (testObj.properties[kCallback])
386 testObj.properties[kCallback](testObj.sample); 406 testObj.properties[kCallback](testObj.sample);
387 else 407 else
388 shouldRemoveSample = true; 408 shouldRemoveSample = true;
389 } else { 409 } else {
390 if (window.testRunner) 410 if (window.testRunner)
391 shouldRemoveSample = true; 411 shouldRemoveSample = true;
392 } 412 }
393 413
394 if (shouldRemoveSample) 414 if (shouldRemoveSample)
395 testObj.sample.remove(); 415 testObj.sample.remove();
396 416
397 // This is the earliest timing when a new spellcheck_test can be started. 417 // We may be in a spellCheckResolvedCallback here, so removing the callback
398 spellcheckTestRunning = false; 418 // (and hence, all remaining tasks) must be done asynchronously.
419 setTimeout(() => {
420 if (window.testRunner)
421 testRunner.removeSpellCheckResolvedCallback();
399 422
400 /** @type {Object} */ 423 // This is the earliest timing when a new spellcheck_test can be started.
401 const next = testQueue.shift(); 424 spellcheckTestRunning = false;
402 if (next === undefined) 425
403 return; 426 /** @type {Object} */
404 invokeSpellcheckTest(next.testObject, next.input, 427 const next = testQueue.shift();
405 next.tester, next.expectedText); 428 if (next === undefined)
429 return;
430 invokeSpellcheckTest(next.testObject, next.input,
431 next.tester, next.expectedText);
432 }, 0);
406 }); 433 });
407 434
408 /** 435 /**
409 * @param {Object=} passedArgs 436 * @param {Object=} passedArgs
410 * @return {!Object} 437 * @return {!Object}
411 */ 438 */
412 function getTestArguments(passedArgs) { 439 function getTestArguments(passedArgs) {
413 const args = {}; 440 const args = {};
414 args[kIsSpellcheckTest] = true; 441 args[kIsSpellcheckTest] = true;
415 [kTitle, kCallback].forEach(key => args[key] = undefined); 442 [kTitle, kCallback].forEach(key => args[key] = undefined);
(...skipping 30 matching lines...) Expand all
446 tester: tester, expectedText: expectedText}); 473 tester: tester, expectedText: expectedText});
447 return; 474 return;
448 } 475 }
449 476
450 invokeSpellcheckTest(testObject, input, tester, expectedText); 477 invokeSpellcheckTest(testObject, input, tester, expectedText);
451 } 478 }
452 479
453 // Export symbols 480 // Export symbols
454 window.spellcheck_test = spellcheckTest; 481 window.spellcheck_test = spellcheckTest;
455 })(); 482 })();
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698