forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			283 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			283 lines
		
	
	
	
		
			8.2 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* Any copyright is dedicated to the Public Domain.
 | |
|  * http://creativecommons.org/publicdomain/zero/1.0/ */
 | |
| 
 | |
| /* eslint-env mozilla/frame-script */
 | |
| 
 | |
| ChromeUtils.import("resource://gre/modules/AddonManager.jsm", this);
 | |
| ChromeUtils.import("resource://gre/modules/PerformanceWatcher.jsm", this);
 | |
| ChromeUtils.import("resource://gre/modules/Services.jsm", this);
 | |
| ChromeUtils.import("resource://testing-common/ContentTaskUtils.jsm", this);
 | |
| 
 | |
| /**
 | |
|  * Base class for simulating slow addons/webpages.
 | |
|  */
 | |
| function CPUBurner(url, jankThreshold) {
 | |
|   info(`CPUBurner: Opening tab for ${url}\n`);
 | |
|   this.url = url;
 | |
|   this.tab = BrowserTestUtils.addTab(gBrowser, url);
 | |
|   this.jankThreshold = jankThreshold;
 | |
|   let browser = this.tab.linkedBrowser;
 | |
|   this._browser = browser;
 | |
|   ContentTask.spawn(this._browser, null, CPUBurner.frameScript);
 | |
|   this.promiseInitialized = BrowserTestUtils.browserLoaded(browser);
 | |
| }
 | |
| CPUBurner.prototype = {
 | |
|   get windowId() {
 | |
|     return this._browser.outerWindowID;
 | |
|   },
 | |
|   /**
 | |
|    * Burn CPU until it triggers a listener with the specified jank threshold.
 | |
|    */
 | |
|   async run(burner, max, listener) {
 | |
|     listener.reset();
 | |
|     for (let i = 0; i < max; ++i) {
 | |
|       await new Promise(resolve => setTimeout(resolve, 50));
 | |
|       try {
 | |
|         await this[burner]();
 | |
|       } catch (ex) {
 | |
|         return false;
 | |
|       }
 | |
|       if (listener.triggered && listener.result >= this.jankThreshold) {
 | |
|         return true;
 | |
|       }
 | |
|     }
 | |
|     return false;
 | |
|   },
 | |
|   dispose() {
 | |
|     info(`CPUBurner: Closing tab for ${this.url}\n`);
 | |
|     gBrowser.removeTab(this.tab);
 | |
|   }
 | |
| };
 | |
| // This function is injected in all frames
 | |
| CPUBurner.frameScript = function() {
 | |
|   try {
 | |
|     "use strict";
 | |
| 
 | |
|     let sandboxes = new Map();
 | |
|     let getSandbox = function(addonId) {
 | |
|       let sandbox = sandboxes.get(addonId);
 | |
|       if (!sandbox) {
 | |
|         sandbox = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), { addonId  });
 | |
|         sandboxes.set(addonId, sandbox);
 | |
|       }
 | |
|       return sandbox;
 | |
|     };
 | |
| 
 | |
|     let burnCPU = function() {
 | |
|       var start = Date.now();
 | |
|       var ignored = [];
 | |
|       while (Date.now() - start < 500) {
 | |
|         ignored[ignored.length % 2] = ignored.length;
 | |
|       }
 | |
|     };
 | |
|     let burnCPUInSandbox = function(addonId) {
 | |
|       let sandbox = getSandbox(addonId);
 | |
|       Cu.evalInSandbox(burnCPU.toSource() + "()", sandbox);
 | |
|     };
 | |
| 
 | |
|     {
 | |
|       let topic = "test-performance-watcher:burn-content-cpu";
 | |
|         addMessageListener(topic, function(msg) {
 | |
|         try {
 | |
|           if (msg.data && msg.data.addonId) {
 | |
|             burnCPUInSandbox(msg.data.addonId);
 | |
|           } else {
 | |
|             burnCPU();
 | |
|           }
 | |
|           sendAsyncMessage(topic, {});
 | |
|         } catch (ex) {
 | |
|           dump(`This is the content attempting to burn CPU: error ${ex}\n`);
 | |
|           dump(`${ex.stack}\n`);
 | |
|         }
 | |
|       });
 | |
|     }
 | |
| 
 | |
|     // Bind the function to the global context or it might be GC'd during test
 | |
|     // causing failures (bug 1230027)
 | |
|     this.burnCPOWInSandbox = function(addonId) {
 | |
|       try {
 | |
|         burnCPUInSandbox(addonId);
 | |
|       } catch (ex) {
 | |
|         dump(`This is the addon attempting to burn CPOW: error ${ex}\n`);
 | |
|         dump(`${ex.stack}\n`);
 | |
|       }
 | |
|     };
 | |
| 
 | |
|     sendAsyncMessage("test-performance-watcher:cpow-init", {}, {
 | |
|       burnCPOWInSandbox: this.burnCPOWInSandbox
 | |
|     });
 | |
| 
 | |
|   } catch (ex) {
 | |
|     Cu.reportError("This is the addon: error " + ex);
 | |
|     Cu.reportError(ex.stack);
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Base class for listening to slow group alerts
 | |
|  */
 | |
| function AlertListener(accept, {register, unregister}) {
 | |
|   this.listener = (...args) => {
 | |
|     if (this._unregistered) {
 | |
|       throw new Error("Listener was unregistered");
 | |
|     }
 | |
|     let result = accept(...args);
 | |
|     if (!result) {
 | |
|       return;
 | |
|     }
 | |
|     this.result = result;
 | |
|     this.triggered = true;
 | |
|   };
 | |
|   this.triggered = false;
 | |
|   this.result = null;
 | |
|   this._unregistered = false;
 | |
|   this._unregister = unregister;
 | |
|   registerCleanupFunction(() => {
 | |
|     this.unregister();
 | |
|   });
 | |
|   register();
 | |
| }
 | |
| AlertListener.prototype = {
 | |
|   unregister() {
 | |
|     this.reset();
 | |
|     if (this._unregistered) {
 | |
|       info(`head.js: No need to unregister, we're already unregistered.\n`);
 | |
|       return;
 | |
|     }
 | |
|     info(`head.js: Unregistering listener.\n`);
 | |
|     this._unregistered = true;
 | |
|     this._unregister();
 | |
|     info(`head.js: Unregistration complete.\n`);
 | |
|   },
 | |
|   reset() {
 | |
|     this.triggered = false;
 | |
|     this.result = null;
 | |
|   },
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Simulate a slow add-on.
 | |
|  */
 | |
| function AddonBurner(addonId = "fake add-on id: " + Math.random()) {
 | |
|   this.jankThreshold = 200000;
 | |
|   CPUBurner.call(this, `http://example.com/?uri=${addonId}`, this.jankThreshold);
 | |
|   this._addonId = addonId;
 | |
|   this._sandbox = Cu.Sandbox(Services.scriptSecurityManager.getSystemPrincipal(), { addonId: this._addonId });
 | |
|   this._CPOWBurner = null;
 | |
| 
 | |
|   this._promiseCPOWBurner = new Promise(resolve => {
 | |
|     this._browser.messageManager.addMessageListener("test-performance-watcher:cpow-init", msg => {
 | |
|       // Note that we cannot resolve Promises with CPOWs now that they
 | |
|       // have been outlawed in bug 1233497, so we stash it in the
 | |
|       // AddonBurner instance instead.
 | |
|       this._CPOWBurner = msg.objects.burnCPOWInSandbox;
 | |
|       resolve();
 | |
|     });
 | |
|   });
 | |
| }
 | |
| AddonBurner.prototype = Object.create(CPUBurner.prototype);
 | |
| Object.defineProperty(AddonBurner.prototype, "addonId", {
 | |
|   get() {
 | |
|     return this._addonId;
 | |
|   }
 | |
| });
 | |
| 
 | |
| /**
 | |
|  * Simulate slow code being executed by the add-on in the chrome.
 | |
|  */
 | |
| AddonBurner.prototype.burnCPU = function() {
 | |
|   Cu.evalInSandbox(AddonBurner.burnCPU.toSource() + "()", this._sandbox);
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Simulate slow code being executed by the add-on in a CPOW.
 | |
|  */
 | |
| AddonBurner.prototype.promiseBurnCPOW = async function() {
 | |
|   await this._promiseCPOWBurner;
 | |
|   ok(this._CPOWBurner, "Got the CPOW burner");
 | |
|   let burner = this._CPOWBurner;
 | |
|   info("Parent: Preparing to burn CPOW");
 | |
|   try {
 | |
|     await burner(this._addonId);
 | |
|     info("Parent: Done burning CPOW");
 | |
|   } catch (ex) {
 | |
|     info(`Parent: Error burning CPOW: ${ex}\n`);
 | |
|     info(ex.stack + "\n");
 | |
|   }
 | |
| };
 | |
| 
 | |
| /**
 | |
|  * Simulate slow code being executed by the add-on in the content.
 | |
|  */
 | |
| AddonBurner.prototype.promiseBurnContentCPU = function() {
 | |
|   return promiseContentResponse(this._browser, "test-performance-watcher:burn-content-cpu", {addonId: this._addonId});
 | |
| };
 | |
| AddonBurner.burnCPU = function() {
 | |
|   var start = Date.now();
 | |
|   var ignored = [];
 | |
|   while (Date.now() - start < 500) {
 | |
|     ignored[ignored.length % 2] = ignored.length;
 | |
|   }
 | |
| };
 | |
| 
 | |
| 
 | |
| function AddonListener(addonId, accept) {
 | |
|   let target = {addonId};
 | |
|   AlertListener.call(this, accept, {
 | |
|     register: () => {
 | |
|       info(`AddonListener: registering ${JSON.stringify(target, null, "\t")}`);
 | |
|       PerformanceWatcher.addPerformanceListener({addonId}, this.listener);
 | |
|     },
 | |
|     unregister: () => {
 | |
|       info(`AddonListener: unregistering ${JSON.stringify(target, null, "\t")}`);
 | |
|       PerformanceWatcher.removePerformanceListener({addonId}, this.listener);
 | |
|     }
 | |
|   });
 | |
| }
 | |
| AddonListener.prototype = Object.create(AlertListener.prototype);
 | |
| 
 | |
| function promiseContentResponse(browser, name, message) {
 | |
|   let mm = browser.messageManager;
 | |
|   let promise = new Promise(resolve => {
 | |
|     function removeListener() {
 | |
|       mm.removeMessageListener(name, listener);
 | |
|     }
 | |
| 
 | |
|     function listener(msg) {
 | |
|       removeListener();
 | |
|       resolve(msg.data);
 | |
|     }
 | |
| 
 | |
|     mm.addMessageListener(name, listener);
 | |
|     registerCleanupFunction(removeListener);
 | |
|   });
 | |
|   mm.sendAsyncMessage(name, message);
 | |
|   return promise;
 | |
| }
 | |
| function promiseContentResponseOrNull(browser, name, message) {
 | |
|   if (!browser.messageManager) {
 | |
|     return null;
 | |
|   }
 | |
|   return promiseContentResponse(browser, name, message);
 | |
| }
 | |
| 
 | |
| /**
 | |
|  * `true` if we are running an OS in which the OS performance
 | |
|  * clock has a low precision and might unpredictably
 | |
|  * never be updated during the execution of the test.
 | |
|  */
 | |
| function hasLowPrecision() {
 | |
|   let [sysName, sysVersion] = [Services.sysinfo.getPropertyAsAString("name"), Services.sysinfo.getPropertyAsDouble("version")];
 | |
|   info(`Running ${sysName} version ${sysVersion}`);
 | |
| 
 | |
|   if (sysName == "Windows_NT" && sysVersion < 6) {
 | |
|     info("Running old Windows, need to deactivate tests due to bad precision.");
 | |
|     return true;
 | |
|   }
 | |
|   if (sysName == "Linux" && sysVersion <= 2.6) {
 | |
|     info("Running old Linux, need to deactivate tests due to bad precision.");
 | |
|     return true;
 | |
|   }
 | |
|   info("This platform has good precision.");
 | |
|   return false;
 | |
| }
 | 
