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,
							 | 
						||
| 
								 | 
							
								};
							 |