You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					110 lines
				
				2.7 KiB
			
		
		
			
		
	
	
					110 lines
				
				2.7 KiB
			| 
											3 years ago
										 | 'use strict'; | ||
|  | 
 | ||
|  | const assert = require('assert'); | ||
|  | const { inspect } = require('util'); | ||
|  | 
 | ||
|  | const mustCallChecks = []; | ||
|  | 
 | ||
|  | function noop() {} | ||
|  | 
 | ||
|  | function runCallChecks(exitCode) { | ||
|  |   if (exitCode !== 0) return; | ||
|  | 
 | ||
|  |   const failed = mustCallChecks.filter((context) => { | ||
|  |     if ('minimum' in context) { | ||
|  |       context.messageSegment = `at least ${context.minimum}`; | ||
|  |       return context.actual < context.minimum; | ||
|  |     } | ||
|  |     context.messageSegment = `exactly ${context.exact}`; | ||
|  |     return context.actual !== context.exact; | ||
|  |   }); | ||
|  | 
 | ||
|  |   failed.forEach((context) => { | ||
|  |     console.error('Mismatched %s function calls. Expected %s, actual %d.', | ||
|  |                   context.name, | ||
|  |                   context.messageSegment, | ||
|  |                   context.actual); | ||
|  |     console.error(context.stack.split('\n').slice(2).join('\n')); | ||
|  |   }); | ||
|  | 
 | ||
|  |   if (failed.length) | ||
|  |     process.exit(1); | ||
|  | } | ||
|  | 
 | ||
|  | function mustCall(fn, exact) { | ||
|  |   return _mustCallInner(fn, exact, 'exact'); | ||
|  | } | ||
|  | 
 | ||
|  | function mustCallAtLeast(fn, minimum) { | ||
|  |   return _mustCallInner(fn, minimum, 'minimum'); | ||
|  | } | ||
|  | 
 | ||
|  | function _mustCallInner(fn, criteria = 1, field) { | ||
|  |   if (process._exiting) | ||
|  |     throw new Error('Cannot use common.mustCall*() in process exit handler'); | ||
|  | 
 | ||
|  |   if (typeof fn === 'number') { | ||
|  |     criteria = fn; | ||
|  |     fn = noop; | ||
|  |   } else if (fn === undefined) { | ||
|  |     fn = noop; | ||
|  |   } | ||
|  | 
 | ||
|  |   if (typeof criteria !== 'number') | ||
|  |     throw new TypeError(`Invalid ${field} value: ${criteria}`); | ||
|  | 
 | ||
|  |   const context = { | ||
|  |     [field]: criteria, | ||
|  |     actual: 0, | ||
|  |     stack: inspect(new Error()), | ||
|  |     name: fn.name || '<anonymous>' | ||
|  |   }; | ||
|  | 
 | ||
|  |   // Add the exit listener only once to avoid listener leak warnings
 | ||
|  |   if (mustCallChecks.length === 0) | ||
|  |     process.on('exit', runCallChecks); | ||
|  | 
 | ||
|  |   mustCallChecks.push(context); | ||
|  | 
 | ||
|  |   function wrapped(...args) { | ||
|  |     ++context.actual; | ||
|  |     return fn.call(this, ...args); | ||
|  |   } | ||
|  |   // TODO: remove origFn?
 | ||
|  |   wrapped.origFn = fn; | ||
|  | 
 | ||
|  |   return wrapped; | ||
|  | } | ||
|  | 
 | ||
|  | function getCallSite(top) { | ||
|  |   const originalStackFormatter = Error.prepareStackTrace; | ||
|  |   Error.prepareStackTrace = (err, stack) => | ||
|  |     `${stack[0].getFileName()}:${stack[0].getLineNumber()}`; | ||
|  |   const err = new Error(); | ||
|  |   Error.captureStackTrace(err, top); | ||
|  |   // With the V8 Error API, the stack is not formatted until it is accessed
 | ||
|  |   // eslint-disable-next-line no-unused-expressions
 | ||
|  |   err.stack; | ||
|  |   Error.prepareStackTrace = originalStackFormatter; | ||
|  |   return err.stack; | ||
|  | } | ||
|  | 
 | ||
|  | function mustNotCall(msg) { | ||
|  |   const callSite = getCallSite(mustNotCall); | ||
|  |   return function mustNotCall(...args) { | ||
|  |     args = args.map(inspect).join(', '); | ||
|  |     const argsInfo = (args.length > 0 | ||
|  |                       ? `\ncalled with arguments: ${args}` | ||
|  |                       : ''); | ||
|  |     assert.fail( | ||
|  |       `${msg || 'function should not have been called'} at ${callSite}` | ||
|  |         + argsInfo); | ||
|  |   }; | ||
|  | } | ||
|  | 
 | ||
|  | module.exports = { | ||
|  |   mustCall, | ||
|  |   mustCallAtLeast, | ||
|  |   mustNotCall, | ||
|  | }; |