diff --git a/test/performance/eventTarget.js b/test/performance/eventTarget.js new file mode 100644 index 000000000..0456b32ac --- /dev/null +++ b/test/performance/eventTarget.js @@ -0,0 +1,84 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +(function(_global) { + var testRunner = _global['__zone_symbol__testRunner']; + var mark = _global['__zone_symbol__mark']; + var measure = _global['__zone_symbol__measure']; + var zone = _global['__zone_symbol__callbackZone']; + var button; + var testTarget = { + title: 'addEventListener', + times: 10, + before: function() { + button = document.createElement('button'); + document.body.appendChild(button); + _global['__zone_symbol__callbackContext'].measureName = 'addEventListener_callback'; + _global['__zone_symbol__callbackContext'].type = 'eventTask'; + _global['__zone_symbol__callbackContext'].source = 'addEventListener'; + }, + after: function() { + document.body.removeChild(button); + button = null; + }, + apis: [ + { + supportClear: true, + method: 'addEventListener', + nativeMethod: '__zone_symbol__addEventListener', + clearMethod: 'removeEventListener', + nativeClearMethod: '__zone_symbol__removeEventListener', + run: function() { + var listener = function() {}; + button.addEventListener('click', listener); + return listener; + }, + runClear: function(timerId) { + return button.removeEventListener('click', timerId); + }, + nativeRun: function() { + var listener = function() {}; + button['__zone_symbol__addEventListener']('click', listener); + return listener; + }, + nativeRunClear: function(timerId) { + return button['__zone_symbol__removeEventListener']('click', timerId); + } + }, + { + isCallback: true, + supportClear: false, + method: 'addEventListener_callback', + nativeMethod: 'native_addEventListener_callback', + run: function() { + var listener = function() {}; + zone.run(function() { + button.addEventListener('click', listener); + }); + var event = document.createEvent('Event'); + event.initEvent('click', true, true); + button.dispatchEvent(event); + button.removeEventListener('click', listener); + }, + nativeRun: function() { + var func = function() {}; + var listener = function() { + mark('native_addEventListener_callback'); + func.apply(this, arguments); + measure('native_addEventListener_callback', 'native_addEventListener_callback'); + }; + button['__zone_symbol__addEventListener']('click', listener); + var event = document.createEvent('Event'); + event.initEvent('click', true, true); + button.dispatchEvent(event); + button['__zone_symbol__removeEventListener']('click', listener); + } + } + ], + }; + return testRunner(testTarget); +}(typeof window === 'undefined' ? global : window)); diff --git a/test/performance/performance.html b/test/performance/performance.html new file mode 100644 index 000000000..37d5b5943 --- /dev/null +++ b/test/performance/performance.html @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + +
Performance Bencnhmark of Zone.js vs Native Delegate!
+
+
+ + + + + + +
+ Module + + API + + Performance overhead +
+
+
+ + + diff --git a/test/performance/performance_setup.js b/test/performance/performance_setup.js new file mode 100644 index 000000000..4ec59891b --- /dev/null +++ b/test/performance/performance_setup.js @@ -0,0 +1,298 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +(function(_global) { + var allTasks = _global['__zone_symbol__performance_tasks']; + if (!allTasks) { + allTasks = _global['__zone_symbol__performance_tasks'] = []; + } + + var mark = _global['__zone_symbol__mark'] = function(name) { + performance && performance['mark'] && performance['mark'](name); + }; + + var measure = _global['__zone_symbol__measure'] = function(name, label) { + performance && performance['measure'] && performance['measure'](name, label); + }; + + var getEntries = _global['__zone_symbol__getEntries'] = function() { + performance && performance['getEntries'] && performance['getEntries'](); + }; + + var getEntriesByName = _global['__zone_symbol__getEntriesByName'] = function(name) { + return performance && performance['getEntriesByName'] && performance['getEntriesByName'](name); + }; + + var clearMarks = _global['__zone_symbol__clearMarks'] = function(name) { + return performance && performance['clearMarks'] && performance['clearMarks'](name); + }; + + var clearMeasures = _global['__zone_symbol__clearMeasures'] = function(name) { + return performance && performance['clearMeasures'] && performance['clearMeasures'](name); + }; + + var averageMeasures = _global['__zone_symbol__averageMeasures'] = function(name, times) { + var sum = _global['__zone_symbol__getEntriesByName'](name) + .filter(function(m) { + return m.entryType === 'measure'; + }) + .map(function(m) { + return m.duration + }) + .reduce(function(sum, d) { + return sum + d; + }); + return sum / times; + }; + + var serialPromise = _global['__zone_symbol__serialPromise'] = + function(promiseFactories) { + let lastPromise; + for (var i = 0; i < promiseFactories.length; i++) { + var promiseFactory = promiseFactories[i]; + if (!lastPromise) { + lastPromise = promiseFactory.factory(promiseFactory.context).then(function(value) { + return {value, idx: 0}; + }); + } else { + lastPromise = lastPromise.then(function(ctx) { + var idx = ctx.idx + 1; + var promiseFactory = promiseFactories[idx]; + return promiseFactory.factory(promiseFactory.context).then(function(value) { + return {value, idx}; + }); + }); + } + } + return lastPromise; + } + + var callbackContext = _global['__zone_symbol__callbackContext'] = {}; + var zone = _global['__zone_symbol__callbackZone'] = Zone.current.fork({ + name: 'callback', + onScheduleTask: function(delegate, curr, target, task) { + delegate.scheduleTask(target, task); + if (task.type === callbackContext.type && + task.source.indexOf(callbackContext.source) !== -1) { + if (task.type === 'macroTask' || task.type === 'eventTask') { + var invoke = task.invoke; + task.invoke = function() { + mark(callbackContext.measureName); + var result = invoke.apply(this, arguments); + measure(callbackContext.measureName, callbackContext.measureName); + return result; + }; + } else if (task.type === 'microTask') { + var callback = task.callback; + task.callback = function() { + mark(callbackContext.measureName); + var result = callback.apply(this, arguments); + measure(callbackContext.measureName, callbackContext.measureName); + return result; + }; + } + } + return task; + } + }); + + var runAsync = _global['__zone_symbol__runAsync'] = function(testFn, times, _delay) { + var delay = _delay | 100; + const fnPromise = function() { + return new Promise(function(res, rej) { + // run test with a setTimeout + // several times to decrease measurement error + setTimeout(function() { + testFn().then(function() { + res(); + }); + }, delay); + }); + }; + var promiseFactories = []; + for (var i = 0; i < times; i++) { + promiseFactories.push({factory: fnPromise, context: {}}); + } + + return serialPromise(promiseFactories); + }; + + var getNativeMethodName = function(nativeWithSymbol) { + return nativeWithSymbol.replace('__zone_symbol__', 'native_'); + }; + + function testAddRemove(api, count) { + var timerId = []; + + var name = api.method; + mark(name); + for (var i = 0; i < count; i++) { + timerId.push(api.run()); + } + measure(name, name); + + if (api.supportClear) { + var clearName = api.clearMethod; + mark(clearName); + for (var i = 0; i < count; i++) { + api.runClear(timerId[i]); + } + measure(clearName, clearName); + } + + timerId = []; + + var nativeName = getNativeMethodName(api.nativeMethod); + mark(nativeName); + for (var i = 0; i < count; i++) { + timerId.push(api.nativeRun()); + } + measure(nativeName, nativeName); + + if (api.supportClear) { + var nativeClearName = getNativeMethodName(api.nativeClearMethod); + mark(nativeClearName); + for (var i = 0; i < count; i++) { + api.nativeRunClear(timerId[i]); + } + measure(nativeClearName, nativeClearName); + } + + return Promise.resolve(1); + } + + function testCallback(api, count) { + var promises = [Promise.resolve(1)]; + for (var i = 0; i < count; i++) { + var r = api.run(); + if (api.isAsync) { + promises.push(r); + } + } + + for (var i = 0; i < count; i++) { + var r = api.nativeRun(); + if (api.isAsync) { + promises.push(r); + } + } + return Promise.all(promises); + } + + function measureCallback(api, ops) { + var times = ops.times; + var displayText = ops.displayText; + var rawData = ops.rawData; + var summary = ops.summary; + + var name = api.method; + var nativeName = getNativeMethodName(api.nativeMethod); + var measure = averageMeasures(name, times); + var nativeMeasure = averageMeasures(nativeName, times); + displayText += `- ${name} costs ${measure} ms\n`; + displayText += `- ${nativeName} costs ${nativeMeasure} ms\n`; + var absolute = Math.floor(1000 * (measure - nativeMeasure)) / 1000; + displayText += `# ${name} is ${absolute}ms slower than ${nativeName}\n`; + rawData[name + '_measure'] = measure; + rawData[nativeName + '_measure'] = nativeMeasure; + summary[name] = absolute + 'ms'; + } + + function measureAddRemove(api, ops) { + var times = ops.times; + var displayText = ops.displayText; + var rawData = ops.rawData; + var summary = ops.summary; + + var name = api.method; + var nativeName = getNativeMethodName(api.nativeMethod); + + var measure = averageMeasures(name, times); + var nativeMeasure = averageMeasures(nativeName, times); + displayText += `- ${name} costs ${measure} ms\n`; + displayText += `- ${nativeName} costs ${nativeMeasure} ms\n`; + var percent = Math.floor(100 * (measure - nativeMeasure) / nativeMeasure); + displayText += `# ${name} is ${percent}% slower than ${nativeName}\n`; + rawData[name + '_measure'] = measure; + rawData[nativeName + '_measure'] = nativeMeasure; + summary[name] = percent + '%'; + if (api.supportClear) { + var clearName = api.clearMethod; + var nativeClearName = getNativeMethodName(api.nativeClearMethod); + var clearMeasure = averageMeasures(clearName, times); + var nativeClearMeasure = averageMeasures(nativeClearName, times); + var clearPercent = Math.floor(100 * (clearMeasure - nativeClearMeasure) / nativeClearMeasure); + displayText += `- ${clearName} costs ${clearMeasure} ms\n`; + displayText += `- ${nativeClearName} costs ${nativeClearMeasure} ms\n`; + displayText += `# ${clearName} is ${clearPercent}% slower than ${nativeClearName}\n`; + rawData[clearName + '_measure'] = clearMeasure; + rawData[nativeClearName + '_measure'] = nativeClearMeasure; + summary[clearName] = clearPercent + '%'; + } + } + + var testRunner = _global['__zone_symbol__testRunner'] = function(testTarget) { + var title = testTarget.title; + var apis = testTarget.apis; + var methods = apis.reduce(function(acc, api) { + return acc.concat([ + api.method, api.nativeMethod + ].concat(api.supportClear ? [api.clearMethod, api.nativeClearMethod] : []) + .concat[api.method + '_callback', api.nativeMethod + '_callback']); + + }, []); + var times = testTarget.times; + + allTasks.push({ + title: title, + cleanFn: function() { + methods.forEach(function(m) { + clearMarks(m); + clearMeasures(m); + }); + }, + before: function() { + testTarget.before && testTarget.before(); + }, + after: function() { + testTarget.after && testTarget.after(); + }, + testFn: function() { + var count = typeof testTarget.count === 'number' ? testTarget.count : 10000; + var times = typeof testTarget.times === 'number' ? testTarget.times : 5; + + var testFunction = function() { + var promises = []; + apis.forEach(function(api) { + if (api.isCallback) { + var r = testCallback(api, count / 100); + promises.push(api.isAsync ? r : Promise.resolve(1)); + } else { + var r = testAddRemove(api, count); + promises.push[api.isAsync ? r : Promise.resolve(1)]; + } + }); + return Promise.all(promises); + }; + + return runAsync(testFunction, times).then(function() { + var displayText = `running ${count} times\n`; + var rawData = {}; + var summary = {}; + apis.forEach(function(api) { + if (api.isCallback) { + measureCallback(api, {times, displayText, rawData, summary}); + } else { + measureAddRemove(api, {times, displayText, rawData, summary}); + } + }); + return Promise.resolve({displayText: displayText, rawData: rawData, summary: summary}); + }); + } + }); + }; +}(typeof window === 'undefined' ? global : window)); diff --git a/test/performance/performance_ui.js b/test/performance/performance_ui.js new file mode 100644 index 000000000..5a6140053 --- /dev/null +++ b/test/performance/performance_ui.js @@ -0,0 +1,162 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +(function(_global) { + var options; + + function setAttributes(elem, attrs) { + if (!attrs) { + return; + } + Object.keys(attrs).forEach(function(key) { + elem.setAttribute(key, attrs[key]); + }); + } + + function createLi(attrs) { + var li = document.createElement('li'); + setAttributes(li, attrs); + return li; + } + + function createLabel(attrs) { + var label = document.createElement('label'); + setAttributes(label, attrs); + return label; + } + + function createButton(attrs, innerHtml) { + var button = document.createElement('button'); + button.innerHTML = innerHtml; + setAttributes(button, attrs); + return button; + } + + function createTextNode(text) { + return document.createTextNode(text); + } + + function createCheckbox(attrs, checked) { + var checkbox = document.createElement('input'); + checkbox.type = 'checkbox'; + checkbox.checked = !!checked; + setAttributes(checkbox, attrs); + return checkbox; + } + + function createUl(attrs) { + var ul = document.createElement('ul'); + setAttributes(ul, attrs); + return ul; + } + + var serailPromise = _global['__zone_symbol__serialPromise']; + + _global['__zone_symbol__testTargetsUIBuild'] = function(_options) { + options = _options; + var allButton = createButton({}, 'test selected'); + allButton.addEventListener('click', function() { + var promiseFactories = []; + for (var i = 0; i < options.tests.length; i++) { + var checkbox = document.getElementById('testcheck' + i); + if (checkbox.checked) { + var test = options.tests[i]; + promiseFactories.push({ + factory: function(context) { + return doTest(context.test, context.idx); + }, + context: {test: test, idx: i} + }); + } + } + serailPromise(promiseFactories); + }); + options.targetContainer.appendChild(allButton); + + var ul = createUl(); + options.targetContainer.appendChild(ul); + + for (var i = 0; i < options.tests.length; i++) { + buildTestItemUI(ul, options.tests[i], i); + } + }; + + function buildTestItemUI(ul, testItem, idx) { + var li = createLi({'id': 'test' + idx}); + + var button = createButton({'id': 'buttontest' + idx}, 'begin test'); + buildButtonClickHandler(button); + + var title = createTextNode(options.tests[idx].title); + var checkbox = createCheckbox({'id': 'testcheck' + idx}, true); + var label = createLabel({'id': 'label' + idx}); + + li.appendChild(checkbox); + li.appendChild(title); + li.appendChild(button); + li.appendChild(label); + + ul.appendChild(li); + } + + function processTestResult(test, result, id) { + var split = result.displayText.split('\n'); + options.jsonResult[test.title] = result.rawData; + options.jsonContainer.innerHTML = + '
' + JSON.stringify(options.jsonResult) + '
'; + + var summary = result.summary; + var row = options.resultsContainer.insertRow(); + var cell = row.insertCell(); + cell.innerHTML = test.title; + cell.rowSpan = Object.keys(summary).length; + var idx = 0; + Object.keys(summary).forEach(function(key) { + var tableRow = row; + if (idx !== 0) { + tableRow = options.resultsContainer.insertRow(); + } + var keyCell = tableRow.insertCell(); + keyCell.innerHTML = key; + var valueCell = tableRow.insertCell(); + valueCell.innerHTML = summary[key]; + idx++; + }); + + var testLi = document.getElementById('test' + id); + for (var j = 0; j < split.length; j++) { + var br = document.createElement('br'); + var s = document.createTextNode(split[j]); + testLi.appendChild(br); + testLi.appendChild(s); + } + } + + function doTest(test, id) { + test.cleanFn(); + test.before(); + var button = document.getElementById('buttontest' + id); + button.setAttribute('enabled', 'false'); + var label = document.getElementById('label' + id); + label.innerHTML = 'Testing'; + return test.testFn().then(function(result) { + processTestResult(test, result, id); + test.after(); + label.innerHTML = 'Finished'; + button.setAttribute('enabled', 'true'); + }); + } + + function buildButtonClickHandler(button) { + button.onclick = function(event) { + var target = event.target; + var id = target.getAttribute('id').substring(10); + var test = options.tests[id]; + doTest(test, id); + }; + } +}(typeof window === 'undefined' ? global : window)); diff --git a/test/performance/promise.js b/test/performance/promise.js new file mode 100644 index 000000000..6657c4cd9 --- /dev/null +++ b/test/performance/promise.js @@ -0,0 +1,63 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +(function(_global) { + var mark = _global['__zone_symbol__mark']; + var measure = _global['__zone_symbol__measure']; + var testRunner = _global['__zone_symbol__testRunner']; + var zone = _global['__zone_symbol__callbackZone']; + var nativePromise = _global['__zone_symbol__Promise']; + var resolved = Promise.resolve(1); + var nativeResolved = nativePromise.resolve(1); + var testTarget = { + title: 'Promise', + times: 10, + before: function() { + _global['__zone_symbol__callbackContext'].measureName = 'Promise_callback'; + _global['__zone_symbol__callbackContext'].type = 'microTask'; + _global['__zone_symbol__callbackContext'].source = 'Promise.then'; + }, + apis: [ + { + supportClear: false, + isAsync: true, + method: 'Promise', + nativeMethod: 'native_Promise', + run: function() { + return resolved.then(function() {}); + }, + nativeRun: function() { + return nativeResolved['__zone_symbol__then'](function() {}); + }, + }, + { + isCallback: true, + isAsync: true, + supportClear: false, + method: 'Promise_callback', + nativeMethod: 'native_Promise_callback', + run: function() { + return zone.run(function() { + return Promise.resolve(1).then(function(v) { + return v; + }); + }); + }, + nativeRun: function() { + var func = function() {}; + return _global['__zone_symbol__Promise'].resolve(1)['__zone_symbol__then'](function() { + mark('native_Promise_callback'); + var result = func.apply(this, arguments); + measure('native_Promise_callback', 'native_Promise_callback'); + return result; + }); + } + } + ], + }; + return testRunner(testTarget); +}(typeof window === 'undefined' ? global : window)); diff --git a/test/performance/requestAnimationFrame.js b/test/performance/requestAnimationFrame.js new file mode 100644 index 000000000..2bdd8cfcc --- /dev/null +++ b/test/performance/requestAnimationFrame.js @@ -0,0 +1,68 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +(function(_global) { + var mark = _global['__zone_symbol__mark']; + var measure = _global['__zone_symbol__measure']; + var zone = _global['__zone_symbol__callbackZone']; + var testRunner = _global['__zone_symbol__testRunner']; + var raf = _global['requestAnimationFrame']; + var cancel = _global['cancelAnimationFrame']; + var nativeRaf = _global['__zone_symbol__requestAnimationFrame']; + var nativeCancel = _global['__zone_symbol__cancelAnimationFrame']; + var testTarget = { + title: 'requestAnimationFrame', + times: 10, + before: function() { + _global['__zone_symbol__callbackContext'].measureName = 'requestAnimationFrame_callback'; + _global['__zone_symbol__callbackContext'].type = 'macroTask'; + _global['__zone_symbol__callbackContext'].source = 'requestAnimationFrame'; + }, + apis: [ + { + supportClear: true, + method: 'requestAnimationFrame', + nativeMethod: '__zone_symbol__requestAnimationFrame', + clearMethod: 'cancelAnimationFrame', + nativeClearMethod: '__zone_symbol__cancelAnimationFrame', + run: function() { + return raf(function() {}); + }, + runClear: function(timerId) { + return cancel(timerId); + }, + nativeRun: function() { + return nativeRaf(function() {}); + }, + nativeRunClear: function(timerId) { + return nativeCancel(timerId); + } + }, + { + isCallback: true, + supportClear: false, + method: 'requestAnimationFrame_callback', + nativeMethod: 'native_requestAnimationFrame_callback', + run: function() { + zone.run(function() { + raf(function() {}); + }); + }, + nativeRun: function() { + var func = function() {}; + nativeRaf(function() { + mark('native_requestAnimationFrame_callback'); + func.apply(this, arguments); + measure( + 'native_requestAnimationFrame_callback', 'native_requestAnimationFrame_callback'); + }); + } + } + ], + }; + return testRunner(testTarget); +}(typeof window === 'undefined' ? global : window)); diff --git a/test/performance/timeout.js b/test/performance/timeout.js new file mode 100644 index 000000000..71df71abe --- /dev/null +++ b/test/performance/timeout.js @@ -0,0 +1,67 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +(function(_global) { + var mark = _global['__zone_symbol__mark']; + var measure = _global['__zone_symbol__measure']; + var testRunner = _global['__zone_symbol__testRunner']; + var setTimeout = _global['setTimeout']; + var clearTimeout = _global['clearTimeout']; + var nativeSetTimeout = _global['__zone_symbol__setTimeout']; + var nativeClearTimeout = _global['__zone_symbol__clearTimeout']; + var zone = _global['__zone_symbol__callbackZone']; + var testTarget = { + title: 'timer', + times: 10, + before: function() { + _global['__zone_symbol__callbackContext'].measureName = 'setTimeout_callback'; + _global['__zone_symbol__callbackContext'].type = 'macroTask'; + _global['__zone_symbol__callbackContext'].source = 'setTimeout'; + }, + apis: [ + { + supportClear: true, + method: 'setTimeout', + nativeMethod: '__zone_symbol__setTimeout', + clearMethod: 'clearTimeout', + nativeClearMethod: '__zone_symbol__clearTimeout', + run: function() { + return setTimeout(function() {}); + }, + runClear: function(timerId) { + return clearTimeout(timerId); + }, + nativeRun: function() { + return nativeSetTimeout(function() {}); + }, + nativeRunClear: function(timerId) { + return nativeClearTimeout(timerId); + } + }, + { + isCallback: true, + supportClear: false, + method: 'setTimeout_callback', + nativeMethod: 'native_setTimeout_callback', + run: function() { + zone.run(function() { + setTimeout(function() {}); + }); + }, + nativeRun: function() { + var func = function() {}; + nativeSetTimeout(function() { + mark('native_setTimeout_callback'); + func.apply(this, arguments); + measure('native_setTimeout_callback', 'native_setTimeout_callback'); + }); + } + } + ], + }; + return testRunner(testTarget); +}(typeof window === 'undefined' ? global : window)); diff --git a/test/performance/xhr.js b/test/performance/xhr.js new file mode 100644 index 000000000..c507a058c --- /dev/null +++ b/test/performance/xhr.js @@ -0,0 +1,51 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +(function(_global) { + var mark = _global['__zone_symbol__mark']; + var measure = _global['__zone_symbol__measure']; + var testRunner = _global['__zone_symbol__testRunner']; + var zone = _global['__zone_symbol__callbackZone']; + var testTarget = { + title: 'xhr', + times: 3, + count: 1000, + before: function() { + _global['__zone_symbol__callbackContext'].measureName = 'xhr_callback'; + _global['__zone_symbol__callbackContext'].type = 'macroTask'; + _global['__zone_symbol__callbackContext'].source = 'send'; + }, + apis: [ + { + supportClear: true, + method: 'XHR.send', + nativeMethod: 'native.XHR.send', + clearMethod: 'XHR.abort', + nativeClearMethod: 'native.XHR.abort', + run: function() { + var xhr = new XMLHttpRequest(); + xhr.open('get', 'http://localhost:8080', true); + xhr.send(); + return xhr; + }, + runClear: function(xhr) { + xhr.abort(); + }, + nativeRun: function() { + var xhr = new XMLHttpRequest(); + xhr['__zone_symbol__open']('get', 'http://localhost:8080', true); + xhr['__zone_symbol__send'](); + return xhr; + }, + nativeRunClear: function(xhr) { + xhr['__zone_symbol__abort'](); + } + }, + ], + }; + return testRunner(testTarget); +}(typeof window === 'undefined' ? global : window));