View source  
  <?php
namespace Drupal\Test;
use PHP_CodeSniffer\Config;
use PHP_CodeSniffer\Ruleset;
use PHP_CodeSniffer\Files\LocalFile;
use PHP_CodeSniffer\RuntimeException;
use PHP_CodeSniffer\Util\Common;
use PHP_CodeSniffer\Autoload;
use PHP_CodeSniffer\Util\Tokens;
abstract class CoderSniffUnitTest extends \PHPUnit_Framework_TestCase {
  
  protected $backupGlobals = false;
  
  public $standardsDir = null;
  
  public $testsDir = null;
  
  protected function setUp() {
    $class = get_class($this);
    $this->rootDir = __DIR__ . '/../../';
    $this->testsDir = __DIR__ . '/';
    $this->standardsDir = __DIR__ . '/../';
    
    $tokens = new Tokens();
    if (!defined('PHP_CODESNIFFER_VERBOSITY')) {
      define('PHP_CODESNIFFER_VERBOSITY', 0);
    }
    if (!defined('PHP_CODESNIFFER_CBF')) {
      define('PHP_CODESNIFFER_CBF', 0);
    }
  }
  
  
  protected function getTestFiles($testFileBase) {
    $testFiles = array();
    $dir = substr($testFileBase, 0, strrpos($testFileBase, DIRECTORY_SEPARATOR));
    $di = new \DirectoryIterator($dir);
    foreach ($di as $file) {
      $path = $file
        ->getPathname();
      if (substr($path, 0, strlen($testFileBase)) === $testFileBase) {
        if ($path !== $testFileBase . 'php' && substr($path, -5) !== 'fixed') {
          $testFiles[] = $path;
        }
      }
    }
    
    sort($testFiles);
    return $testFiles;
  }
  
  
  protected function shouldSkipTest() {
    return false;
  }
  
  
  public final function testSniff() {
    
    if ($this
      ->shouldSkipTest() === true) {
      $this
        ->markTestSkipped();
    }
    $sniffCode = Common::getSniffCode(get_class($this));
    list($standardName, $categoryName, $sniffName) = explode('.', $sniffCode);
    
    if ($this
      ->allSniffCodes()) {
      list($standardName) = explode('\\', get_class($this));
    }
    $testFileBase = $this->rootDir . $standardName . DIRECTORY_SEPARATOR . 'Test' . DIRECTORY_SEPARATOR . $categoryName . DIRECTORY_SEPARATOR . $sniffName . 'UnitTest.';
    $this->standardsDir = $this->rootDir . $standardName . DIRECTORY_SEPARATOR;
    
    $testFiles = $this
      ->getTestFiles($testFileBase);
    $config = new Config();
    $config->cache = false;
    $GLOBALS['PHP_CODESNIFFER_CONFIG'] = $config;
    
    $config->ignored = array();
    $config->standards = array(
      $this->standardsDir,
    );
    $failureMessages = array();
    foreach ($testFiles as $testFile) {
      
      if ($this
        ->allSniffCodes()) {
        $config->sniffs = [];
      }
      else {
        $config->sniffs = array(
          $sniffCode,
        );
      }
      $ruleset = new Ruleset($config);
      $GLOBALS['PHP_CODESNIFFER_RULESET'] = $ruleset;
      $filename = basename($testFile);
      $oldConfig = $config
        ->getSettings();
      try {
        $this
          ->setCliValues($filename, $config);
        $phpcsFile = new LocalFile($testFile, $ruleset, $config);
        $phpcsFile
          ->process();
      } catch (RuntimeException $e) {
        $this
          ->fail('An unexpected exception has been caught: ' . $e
          ->getMessage());
      }
      $failures = $this
        ->generateFailureMessages($phpcsFile);
      $failureMessages = array_merge($failureMessages, $failures);
      if ($phpcsFile
        ->getFixableCount() > 0) {
        
        $config->sniffs = [];
        $ruleset = new Ruleset($config);
        try {
          $this
            ->setCliValues($filename, $config);
          $phpcsFile = new LocalFile($testFile, $ruleset, $config);
          $phpcsFile
            ->process();
        } catch (RuntimeException $e) {
          $this
            ->fail('An unexpected exception has been caught: ' . $e
            ->getMessage());
        }
        
        $phpcsFile->fixer
          ->fixFile();
        $fixable = $phpcsFile
          ->getFixableCount();
        if ($fixable > 0) {
          $failureMessages[] = "Failed to fix {$fixable} fixable violations in {$filename}";
        }
        
        $fixedFile = $testFile . '.fixed';
        if (file_exists($fixedFile) === true) {
          $diff = $phpcsFile->fixer
            ->generateDiff($fixedFile);
          if (trim($diff) !== '') {
            $filename = basename($testFile);
            $fixedFilename = basename($fixedFile);
            $failureMessages[] = "Fixed version of {$filename} does not match expected version in {$fixedFilename}; the diff is\n{$diff}";
          }
        }
      }
      
      $config
        ->setSettings($oldConfig);
    }
    
    if (empty($failureMessages) === false) {
      $this
        ->fail(implode(PHP_EOL, $failureMessages));
    }
  }
  
  
  public function generateFailureMessages(LocalFile $file) {
    $testFile = $file
      ->getFilename();
    $foundErrors = $file
      ->getErrors();
    $foundWarnings = $file
      ->getWarnings();
    $expectedErrors = $this
      ->getErrorList(basename($testFile));
    $expectedWarnings = $this
      ->getWarningList(basename($testFile));
    if (is_array($expectedErrors) === false) {
      throw new RuntimeException('getErrorList() must return an array');
    }
    if (is_array($expectedWarnings) === false) {
      throw new RuntimeException('getWarningList() must return an array');
    }
    
    $allProblems = array();
    $failureMessages = array();
    $GLOBALS['PHP_CODESNIFFER_SNIFF_CODES'] = [];
    $GLOBALS['PHP_CODESNIFFER_FIXABLE_CODES'] = [];
    foreach ($foundErrors as $line => $lineErrors) {
      foreach ($lineErrors as $column => $errors) {
        if (isset($allProblems[$line]) === false) {
          $allProblems[$line] = array(
            'expected_errors' => 0,
            'expected_warnings' => 0,
            'found_errors' => array(),
            'found_warnings' => array(),
          );
        }
        $foundErrorsTemp = array();
        foreach ($allProblems[$line]['found_errors'] as $foundError) {
          $foundErrorsTemp[] = $foundError;
        }
        $errorsTemp = array();
        foreach ($errors as $foundError) {
          $errorsTemp[] = $foundError['message'] . ' (' . $foundError['source'] . ')';
          $source = $foundError['source'];
          if (in_array($source, $GLOBALS['PHP_CODESNIFFER_SNIFF_CODES']) === false) {
            $GLOBALS['PHP_CODESNIFFER_SNIFF_CODES'][] = $source;
          }
          if ($foundError['fixable'] === true && in_array($source, $GLOBALS['PHP_CODESNIFFER_FIXABLE_CODES']) === false) {
            $GLOBALS['PHP_CODESNIFFER_FIXABLE_CODES'][] = $source;
          }
        }
        $allProblems[$line]['found_errors'] = array_merge($foundErrorsTemp, $errorsTemp);
      }
      
      if (isset($expectedErrors[$line]) === true) {
        $allProblems[$line]['expected_errors'] = $expectedErrors[$line];
      }
      else {
        $allProblems[$line]['expected_errors'] = 0;
      }
      unset($expectedErrors[$line]);
    }
    
    foreach ($expectedErrors as $line => $numErrors) {
      if (isset($allProblems[$line]) === false) {
        $allProblems[$line] = array(
          'expected_errors' => 0,
          'expected_warnings' => 0,
          'found_errors' => array(),
          'found_warnings' => array(),
        );
      }
      $allProblems[$line]['expected_errors'] = $numErrors;
    }
    foreach ($foundWarnings as $line => $lineWarnings) {
      foreach ($lineWarnings as $column => $warnings) {
        if (isset($allProblems[$line]) === false) {
          $allProblems[$line] = array(
            'expected_errors' => 0,
            'expected_warnings' => 0,
            'found_errors' => array(),
            'found_warnings' => array(),
          );
        }
        $foundWarningsTemp = array();
        foreach ($allProblems[$line]['found_warnings'] as $foundWarning) {
          $foundWarningsTemp[] = $foundWarning;
        }
        $warningsTemp = array();
        foreach ($warnings as $warning) {
          $warningsTemp[] = $warning['message'] . ' (' . $warning['source'] . ')';
        }
        $allProblems[$line]['found_warnings'] = array_merge($foundWarningsTemp, $warningsTemp);
      }
      
      if (isset($expectedWarnings[$line]) === true) {
        $allProblems[$line]['expected_warnings'] = $expectedWarnings[$line];
      }
      else {
        $allProblems[$line]['expected_warnings'] = 0;
      }
      unset($expectedWarnings[$line]);
    }
    
    foreach ($expectedWarnings as $line => $numWarnings) {
      if (isset($allProblems[$line]) === false) {
        $allProblems[$line] = array(
          'expected_errors' => 0,
          'expected_warnings' => 0,
          'found_errors' => array(),
          'found_warnings' => array(),
        );
      }
      $allProblems[$line]['expected_warnings'] = $numWarnings;
    }
    
    ksort($allProblems);
    foreach ($allProblems as $line => $problems) {
      $numErrors = count($problems['found_errors']);
      $numWarnings = count($problems['found_warnings']);
      $expectedErrors = $problems['expected_errors'];
      $expectedWarnings = $problems['expected_warnings'];
      $errors = '';
      $foundString = '';
      if ($expectedErrors !== $numErrors || $expectedWarnings !== $numWarnings) {
        $lineMessage = "[LINE {$line}]";
        $expectedMessage = 'Expected ';
        $foundMessage = 'in ' . basename($testFile) . ' but found ';
        if ($expectedErrors !== $numErrors) {
          $expectedMessage .= "{$expectedErrors} error(s)";
          $foundMessage .= "{$numErrors} error(s)";
          if ($numErrors !== 0) {
            $foundString .= 'error(s)';
            $errors .= implode(PHP_EOL . ' -> ', $problems['found_errors']);
          }
          if ($expectedWarnings !== $numWarnings) {
            $expectedMessage .= ' and ';
            $foundMessage .= ' and ';
            if ($numWarnings !== 0) {
              if ($foundString !== '') {
                $foundString .= ' and ';
              }
            }
          }
        }
        if ($expectedWarnings !== $numWarnings) {
          $expectedMessage .= "{$expectedWarnings} warning(s)";
          $foundMessage .= "{$numWarnings} warning(s)";
          if ($numWarnings !== 0) {
            $foundString .= 'warning(s)';
            if (empty($errors) === false) {
              $errors .= PHP_EOL . ' -> ';
            }
            $errors .= implode(PHP_EOL . ' -> ', $problems['found_warnings']);
          }
        }
        $fullMessage = "{$lineMessage} {$expectedMessage} {$foundMessage}.";
        if ($errors !== '') {
          $fullMessage .= " The {$foundString} found were:" . PHP_EOL . " -> {$errors}";
        }
        $failureMessages[] = $fullMessage;
      }
      
    }
    
    return $failureMessages;
  }
  
  
  public function setCliValues($filename, $config) {
    return;
  }
  
  
  protected abstract function getErrorList($testFile = NULL);
  
  protected abstract function getWarningList($testFile = NULL);
  
  protected function allSniffCodes() {
    return FALSE;
  }
}