1 /* ***** BEGIN LICENSE BLOCK ***** 2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1 3 * 4 * The contents of this file are subject to the Mozilla Public License Version 5 * 1.1 (the "License"); you may not use this file except in compliance with 6 * the License. You may obtain a copy of the License at 7 * http://www.mozilla.org/MPL/ 8 * 9 * Software distributed under the License is distributed on an "AS IS" basis, 10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 11 * for the specific language governing rights and limitations under the 12 * License. 13 * 14 * The Original Code is MozMill Test code. 15 * 16 * The Initial Developer of the Original Code is the Mozilla Foundation. 17 * Portions created by the Initial Developer are Copyright (C) 2011 18 * the Initial Developer. All Rights Reserved. 19 * 20 * Contributor(s): 21 * Henrik Skupin <mail@hskupin.info> (Original Author) 22 * 23 * Alternatively, the contents of this file may be used under the terms of 24 * either the GNU General Public License Version 2 or later (the "GPL"), or 25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), 26 * in which case the provisions of the GPL or the LGPL are applicable instead 27 * of those above. If you wish to allow use of your version of this file only 28 * under the terms of either the GPL or the LGPL, and not to allow others to 29 * use your version of this file under the terms of the MPL, indicate your 30 * decision by deleting the provisions above and replace them with the notice 31 * and other provisions required by the GPL or the LGPL. If you do not delete 32 * the provisions above, a recipient may use your version of this file under 33 * the terms of any one of the MPL, the GPL or the LGPL. 34 * 35 * ***** END LICENSE BLOCK ***** */ 36 37 /** 38 * @name assertions 39 * @namespace Defines expect and assert methods to be used for assertions. 40 */ 41 var assertions = exports; 42 43 44 // Include necessary modules 45 const { Class } = require("external/inheritance"); 46 const { AssertionError } = require('errors'); 47 48 49 // Use the frame module of Mozmill to raise non-fatal failures 50 var mozmillFrame = {}; 51 Cu.import('resource://mozmill/modules/frame.js', mozmillFrame); 52 53 54 var Expect = Class.create( 55 /** @lends assertions.Expect.prototype */ 56 { 57 /** 58 * The Expect class implements non-fatal assertions, and can be used in cases 59 * when a failing test shouldn't abort the current test function. That allows 60 * to execute multiple tests in a row. 61 * 62 * @class Base class for non-fatal assertions 63 * @constructs 64 * @param {object} [aStartFrame=Components.stack] 65 * Frame to use for logging the test result. If a start frame has been 66 * specified, we walk down the stack until a frame with the same filename 67 * as the start frame has been found. The next file in the stack will be 68 * the frame to use for logging the result. 69 */ 70 initialize: function Expect_initialize(aStartFrame) { 71 this._startFrame = aStartFrame; 72 }, 73 74 /** 75 * Find the frame to use for logging the test result. If a start frame has 76 * been specified, we walk down the stack until a frame with the same filename 77 * as the start frame has been found. The next file in the stack will be the 78 * frame to use for logging the result. 79 * 80 * @returns {object} Frame of the stack to use for logging the result. 81 */ 82 _findCallerFrame: function Expect__findCallerFrame() { 83 let frame = Components.stack; 84 let filename = frame.filename.replace(/(.*)-> /, ""); 85 86 // If a start frame has been specified, walk up the stack until we have 87 // found the corresponding file 88 if (this._startFrame) { 89 filename = this._startFrame.filename.replace(/(.*)-> /, ""); 90 91 while (frame.caller && 92 frame.filename && !frame.filename.match(filename)) { 93 frame = frame.caller; 94 } 95 } 96 97 // Walk even up more until the next file has been found 98 while (frame.caller && 99 (!frame.filename || frame.filename.match(filename))) 100 frame = frame.caller; 101 102 return frame; 103 }, 104 105 /** 106 * Log a test as failing by adding a fail frame. 107 * 108 * @param {object} aResult 109 * Test result details used for reporting. 110 * <dl> 111 * <dd>fileName</dd> 112 * <dt>Name of the file in which the assertion failed.</dt> 113 * <dd>function</dd> 114 * <dt>Function in which the assertion failed.</dt> 115 * <dd>lineNumber</dd> 116 * <dt>Line number of the file in which the assertion failed.</dt> 117 * <dd>message</dd> 118 * <dt>Message why the assertion failed.</dt> 119 * </dl> 120 */ 121 _logFail: function Expect__logFail(aResult) { 122 mozmillFrame.events.fail({fail: aResult}); 123 }, 124 125 /** 126 * Log a test as passing by adding a pass frame. 127 * 128 * @param {object} aResult 129 * Test result details used for reporting. 130 * <dl> 131 * <dd>fileName</dd> 132 * <dt>Name of the file in which the assertion failed.</dt> 133 * <dd>function</dd> 134 * <dt>Function in which the assertion failed.</dt> 135 * <dd>lineNumber</dd> 136 * <dt>Line number of the file in which the assertion failed.</dt> 137 * <dd>message</dd> 138 * <dt>Message why the assertion failed.</dt> 139 * </dl> 140 */ 141 _logPass: function Expect__logPass(aResult) { 142 mozmillFrame.events.pass({pass: aResult}); 143 }, 144 145 /** 146 * Test the condition and mark test as passed or failed 147 * 148 * @param {boolean} aCondition 149 * Condition to test. 150 * @param {string} aMessage 151 * Message to show for the test result 152 * @param {string} aDiagnosis 153 * Diagnose message to show for the test result 154 * @returns {boolean} Result of the test. 155 */ 156 _test: function Expect__test(aCondition, aMessage, aDiagnosis) { 157 let diagnosis = aDiagnosis || ""; 158 let message = aMessage || ""; 159 160 if (aDiagnosis) 161 message = aMessage ? message + " - " + aDiagnosis : aDiagnosis; 162 163 // Build result data 164 let frame = this._findCallerFrame(); 165 let result = { 166 'fileName' : frame.filename.replace(/(.*)-> /, ""), 167 'function' : frame.name, 168 'lineNumber' : frame.lineNumber, 169 'message' : message 170 }; 171 172 // Log test result 173 if (aCondition) 174 this._logPass(result); 175 else 176 this._logFail(result); 177 178 return aCondition; 179 }, 180 181 /** 182 * Perform an always passing test 183 * 184 * @param {string} aMessage 185 * Message to show for the test result. 186 * @returns {boolean} Always returns true. 187 */ 188 pass: function Expect_pass(aMessage) { 189 return this._test(true, aMessage, undefined); 190 }, 191 192 /** 193 * Perform an always failing test 194 * 195 * @param {string} aMessage 196 * Message to show for the test result. 197 * @returns {boolean} Always returns false. 198 */ 199 fail: function Expect_fail(aMessage) { 200 return this._test(false, aMessage, undefined); 201 }, 202 203 /** 204 * Test if the value pass 205 * 206 * @param {boolean|string|number|object} aValue 207 * Value to test. 208 * @param {string} aMessage 209 * Message to show for the test result. 210 * @returns {boolean} Result of the test. 211 */ 212 ok: function Expect_ok(aValue, aMessage) { 213 let condition = !!aValue; 214 let diagnosis = "got '" + aValue + "'"; 215 216 return this._test(condition, aMessage, diagnosis); 217 }, 218 219 /** 220 * Test if both specified values are identical. 221 * 222 * @param {boolean|string|number|object} aValue 223 * Value to test. 224 * @param {boolean|string|number|object} aExpected 225 * Value to strictly compare with. 226 * @param {string} aMessage 227 * Message to show for the test result 228 * @returns {boolean} Result of the test. 229 */ 230 equal: function Expect_equal(aValue, aExpected, aMessage) { 231 let condition = (aValue === aExpected); 232 let diagnosis = "got '" + aValue + "', expected '" + aExpected + "'"; 233 234 return this._test(condition, aMessage, diagnosis); 235 }, 236 237 /** 238 * Test if both specified values are not identical. 239 * 240 * @param {boolean|string|number|object} aValue 241 * Value to test. 242 * @param {boolean|string|number|object} aExpected 243 * Value to strictly compare with. 244 * @param {string} aMessage 245 * Message to show for the test result 246 * @returns {boolean} Result of the test. 247 */ 248 notEqual: function Expect_notEqual(aValue, aExpected, aMessage) { 249 let condition = (aValue !== aExpected); 250 let diagnosis = "got '" + aValue + "', not expected '" + aExpected + "'"; 251 252 return this._test(condition, aMessage, diagnosis); 253 }, 254 255 /** 256 * Test if the regular expression matches the string. 257 * 258 * @param {string} aString 259 * String to test. 260 * @param {RegEx} aRegex 261 * Regular expression to use for testing that a match exists. 262 * @param {string} aMessage 263 * Message to show for the test result 264 * @returns {boolean} Result of the test. 265 */ 266 match: function Expect_match(aString, aRegex, aMessage) { 267 // XXX Bug 634948 268 // Regex objects are transformed to strings when evaluated in a sandbox 269 // For now lets re-create the regex from its string representation 270 let pattern = flags = ""; 271 try { 272 let matches = aRegex.toString().match(/\/(.*)\/(.*)/); 273 274 pattern = matches[1]; 275 flags = matches[2]; 276 } 277 catch (ex) { 278 } 279 280 let regex = new RegExp(pattern, flags); 281 let condition = (aString.match(regex) !== null); 282 let diagnosis = "'" + regex + "' matches for '" + aString + "'"; 283 284 return this._test(condition, aMessage, diagnosis); 285 }, 286 287 /** 288 * Test if the regular expression does not match the string. 289 * 290 * @param {string} aString 291 * String to test. 292 * @param {RegEx} aRegex 293 * Regular expression to use for testing that a match does not exist. 294 * @param {string} aMessage 295 * Message to show for the test result 296 * @returns {boolean} Result of the test. 297 */ 298 notMatch: function Expect_notMatch(aString, aRegex, aMessage) { 299 // XXX Bug 634948 300 // Regex objects are transformed to strings when evaluated in a sandbox 301 // For now lets re-create the regex from its string representation 302 let pattern = flags = ""; 303 try { 304 let matches = aRegex.toString().match(/\/(.*)\/(.*)/); 305 306 pattern = matches[1]; 307 flags = matches[2]; 308 } 309 catch (ex) { 310 } 311 312 let regex = new RegExp(pattern, flags); 313 let condition = (aString.match(regex) === null); 314 let diagnosis = "'" + regex + "' doesn't match for '" + aString + "'"; 315 316 return this._test(condition, aMessage, diagnosis); 317 } 318 }); 319 320 321 var Assert = Class.extend(Expect, 322 /** @lends assertions.Assert */ 323 { 324 /** 325 * The Assert class implements fatal assertions, and can be used in cases 326 * when a failing test has to directly abort the current test function. All 327 * remaining tasks will not be performed. 328 * 329 * @class Base class for fatal assertions 330 * @constructs 331 * @extends assertions.Expect 332 * @requires errors.AssertionError 333 * @param {object} [aStartFrame=Components.stack] 334 * Frame of the stack to start from for the logging. Per default 335 * Components.stack should be used when creating an instance of that class. 336 */ 337 initialize: function Assert(aStartFrame) { 338 this.parent(aStartFrame); 339 }, 340 341 /** 342 * Log a test as failing by throwing an AssertionException. 343 * 344 * @param {object} aResult 345 * Test result details used for reporting. 346 * <dl> 347 * <dd>fileName</dd> 348 * <dt>Name of the file in which the assertion failed.</dt> 349 * <dd>function</dd> 350 * <dt>Function in which the assertion failed.</dt> 351 * <dd>lineNumber</dd> 352 * <dt>Line number of the file in which the assertion failed.</dt> 353 * <dd>message</dd> 354 * <dt>Message why the assertion failed.</dt> 355 * </dl> 356 * @throws {AssertionError } 357 */ 358 _logFail: function Assert__logFail(aResult) { 359 throw new AssertionError(aResult); 360 } 361 }); 362 363 364 // Export of variables 365 assertions.expect = new Expect(Components.stack); 366 assertions.assert = new Assert(Components.stack); 367 368 // Export of classes 369 assertions.Expect = Expect; 370 assertions.Assert = Assert; 371