mutators/stringLiteralsMutator.js

const walk = require('babylon-walk')
const debug = require('debug')('mutode:stringLiteralsMutator')

const mutantRunner = require('../mutantRunner')
const lineDiff = require('../util/lineDiff')

/**
 * @description Mutates string literals values.
 * Strings are mutated to a random string, and to an empty string (if not previously empty).
 * @function stringLiteralsMutator
 * @memberOf module:Mutators
 */
module.exports = async function stringLiteralsMutator ({mutodeInstance, filePath, lines, queue, ast}) {
  debug('Running string literals mutator on %s', filePath)

  walk.ancestor(ast, {
    StringLiteral (node, state, ancestors) {
      const line = node.loc.start.line
      const lineContent = lines[line - 1]

      if (ancestors.length >= 2) {
        const ancestor = ancestors[ancestors.length - 2]
        if (ancestor.type && ancestor.type === 'CallExpression' && ancestor.callee) {
          if (ancestor.callee.type === 'MemberExpression' && ancestor.callee.object.name === 'console') return
          if (ancestor.callee.name) {
            switch (ancestor.callee.name) {
              case 'require':
              case 'debug':
                return
              default:
                break
            }
          }
        }
      }

      if (node.value.length !== 0) {
        const mutantLineContent = lineContent.substr(0, node.loc.start.column) +
          node.extra.raw.replace(node.value, '') +
          lineContent.substr(node.loc.end.column)

        const mutantId = ++mutodeInstance.mutants
        const diff = lineDiff(lineContent, mutantLineContent)
        const log = `MUTANT ${mutantId}:\tSLM Line ${line}:\t${diff}...`
        debug(log)
        mutodeInstance.mutantLog(`MUTANT ${mutantId}:\tSLM ${filePath} Line ${line}:\t\`${lineContent.trim()}\` > \`${mutantLineContent.trim()}'\``)
        const linesCopy = lines.slice()
        linesCopy[line - 1] = mutantLineContent
        const contentToWrite = linesCopy.join('\n')
        queue.push(mutantRunner({mutodeInstance, filePath, contentToWrite, log}))
      }

      const newValue = `'${randomString(node.value.length || 10)}'`
      const mutantLineContent = lineContent.substr(0, node.loc.start.column) +
        newValue +
        lineContent.substr(node.loc.end.column)

      const mutantId = ++mutodeInstance.mutants
      const diff = lineDiff(lineContent, mutantLineContent)
      const log = `MUTANT ${mutantId}:\tSLM Line ${line}:\t${diff}...`
      debug(log)
      mutodeInstance.mutantLog(`MUTANT ${mutantId}:\tSLM ${filePath} Line ${line}:\t\`${lineContent.trim()}\` > \`${mutantLineContent.trim()}'\``)
      const linesCopy = lines.slice()
      linesCopy[line - 1] = mutantLineContent
      const contentToWrite = linesCopy.join('\n')
      queue.push(mutantRunner({mutodeInstance, filePath, contentToWrite, log}))
    }
  })
}

function randomString (length) {
  const chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXTZabcdefghiklmnopqrstuvwxyz'
  let randomstring = ''
  for (let i = 0; i < length; i++) {
    const ind = Math.floor(Math.random() * chars.length)
    randomstring += chars.charAt(ind)
  }
  return randomstring
}