~ubuntu-branches/ubuntu/wily/phabricator/wily

« back to all changes in this revision

Viewing changes to src/lint/linter/__tests__/ArcanistLinterTestCase.php

  • Committer: Package Import Robot
  • Author(s): Richard Sellam
  • Date: 2014-11-01 23:20:06 UTC
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: package-import@ubuntu.com-20141101232006-mvlnp0cil67tsboe
Tags: upstream-0~git20141101/arcanist
Import upstream version 0~git20141101, component arcanist

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
 
 
3
/**
 
4
 * Facilitates implementation of test cases for @{class:ArcanistLinter}s.
 
5
 */
 
6
abstract class ArcanistLinterTestCase extends ArcanistPhutilTestCase {
 
7
 
 
8
  public function executeTestsInDirectory($root, ArcanistLinter $linter) {
 
9
    $files = id(new FileFinder($root))
 
10
      ->withType('f')
 
11
      ->withSuffix('lint-test')
 
12
      ->find();
 
13
 
 
14
    $test_count = 0;
 
15
    foreach ($files as $file) {
 
16
      $this->lintFile($root.$file, $linter);
 
17
      $test_count++;
 
18
    }
 
19
 
 
20
    $this->assertTrue(
 
21
      ($test_count > 0),
 
22
      pht('Expected to find some .lint-test tests in directory %s!', $root));
 
23
  }
 
24
 
 
25
  private function lintFile($file, ArcanistLinter $linter) {
 
26
    $linter = clone $linter;
 
27
 
 
28
    $contents = Filesystem::readFile($file);
 
29
    $contents = preg_split('/^~{4,}\n/m', $contents);
 
30
    if (count($contents) < 2) {
 
31
      throw new Exception(
 
32
        "Expected '~~~~~~~~~~' separating test case and results.");
 
33
    }
 
34
 
 
35
    list ($data, $expect, $xform, $config) = array_merge(
 
36
      $contents,
 
37
      array(null, null));
 
38
 
 
39
    $basename = basename($file);
 
40
 
 
41
    if ($config) {
 
42
      $config = phutil_json_decode($config);
 
43
    } else {
 
44
      $config = array();
 
45
    }
 
46
    PhutilTypeSpec::checkMap(
 
47
      $config,
 
48
      array(
 
49
        'hook' => 'optional bool',
 
50
        'config' => 'optional wild',
 
51
        'path' => 'optional string',
 
52
        'arcconfig' => 'optional map<string, string>',
 
53
      ));
 
54
 
 
55
    $exception = null;
 
56
    $after_lint = null;
 
57
    $messages = null;
 
58
    $exception_message = false;
 
59
    $caught_exception = false;
 
60
 
 
61
    try {
 
62
      $tmp = new TempFile($basename);
 
63
      Filesystem::writeFile($tmp, $data);
 
64
      $full_path = (string)$tmp;
 
65
 
 
66
      $dir = dirname($full_path);
 
67
      $path = basename($full_path);
 
68
      $config_file = null;
 
69
      $arcconfig = idx($config, 'arcconfig');
 
70
      if ($arcconfig) {
 
71
        $config_file = json_encode($arcconfig);
 
72
      }
 
73
 
 
74
      $working_copy = ArcanistWorkingCopyIdentity::newFromRootAndConfigFile(
 
75
        $dir,
 
76
        $config_file,
 
77
        'Unit Test');
 
78
      $configuration_manager = new ArcanistConfigurationManager();
 
79
      $configuration_manager->setWorkingCopyIdentity($working_copy);
 
80
 
 
81
 
 
82
      $engine = new UnitTestableArcanistLintEngine();
 
83
      $engine->setWorkingCopy($working_copy);
 
84
      $engine->setConfigurationManager($configuration_manager);
 
85
      $engine->setPaths(array($path));
 
86
 
 
87
      $engine->setCommitHookMode(idx($config, 'hook', false));
 
88
 
 
89
      $path_name = idx($config, 'path', $path);
 
90
      $linter->addPath($path_name);
 
91
      $linter->addData($path_name, $data);
 
92
      $config = idx($config, 'config', array());
 
93
      foreach ($config as $key => $value) {
 
94
        $linter->setLinterConfigurationValue($key, $value);
 
95
      }
 
96
 
 
97
      $engine->addLinter($linter);
 
98
      $engine->addFileData($path_name, $data);
 
99
 
 
100
      $results = $engine->run();
 
101
 
 
102
      $this->assertEqual(
 
103
        1,
 
104
        count($results),
 
105
        'Expect one result returned by linter.');
 
106
 
 
107
      $result = reset($results);
 
108
      $patcher = ArcanistLintPatcher::newFromArcanistLintResult($result);
 
109
      $after_lint = $patcher->getModifiedFileContent();
 
110
    } catch (ArcanistPhutilTestTerminatedException $ex) {
 
111
      throw $ex;
 
112
    } catch (Exception $exception) {
 
113
      $caught_exception = true;
 
114
      if ($exception instanceof PhutilAggregateException) {
 
115
        $caught_exception = false;
 
116
        foreach ($exception->getExceptions() as $ex) {
 
117
          if ($ex instanceof ArcanistUsageException) {
 
118
            $this->assertSkipped($ex->getMessage());
 
119
          } else {
 
120
            $caught_exception = true;
 
121
          }
 
122
        }
 
123
      } else if ($exception instanceof ArcanistUsageException) {
 
124
        $this->assertSkipped($exception->getMessage());
 
125
      }
 
126
      $exception_message = $exception->getMessage()."\n\n".
 
127
                           $exception->getTraceAsString();
 
128
    }
 
129
 
 
130
    $this->assertEqual(false, $caught_exception, $exception_message);
 
131
    $this->compareLint($basename, $expect, $result);
 
132
    $this->compareTransform($xform, $after_lint);
 
133
  }
 
134
 
 
135
  private function compareLint($file, $expect, ArcanistLintResult $result) {
 
136
    $seen = array();
 
137
    $raised = array();
 
138
    $message_map = array();
 
139
 
 
140
    foreach ($result->getMessages() as $message) {
 
141
      $sev = $message->getSeverity();
 
142
      $line = $message->getLine();
 
143
      $char = $message->getChar();
 
144
      $code = $message->getCode();
 
145
      $name = $message->getName();
 
146
      $message_key = $sev.':'.$line.':'.$char;
 
147
      $message_map[$message_key] = $message;
 
148
      $seen[] = $message_key;
 
149
      $raised[] = "  {$sev} at line {$line}, char {$char}: {$code} {$name}";
 
150
    }
 
151
    $expect = trim($expect);
 
152
    if ($expect) {
 
153
      $expect = explode("\n", $expect);
 
154
    } else {
 
155
      $expect = array();
 
156
    }
 
157
    foreach ($expect as $key => $expected) {
 
158
      $expect[$key] = head(explode(' ', $expected));
 
159
    }
 
160
 
 
161
    $expect = array_fill_keys($expect, true);
 
162
    $seen   = array_fill_keys($seen, true);
 
163
 
 
164
    if (!$raised) {
 
165
      $raised = array('No messages.');
 
166
    }
 
167
    $raised = "Actually raised:\n".implode("\n", $raised);
 
168
 
 
169
    foreach (array_diff_key($expect, $seen) as $missing => $ignored) {
 
170
      list($sev, $line, $char) = explode(':', $missing);
 
171
      $this->assertFailure(
 
172
        "In '{$file}', ".
 
173
        "expected lint to raise {$sev} on line {$line} at char {$char}, ".
 
174
        "but no {$sev} was raised. {$raised}");
 
175
    }
 
176
 
 
177
    foreach (array_diff_key($seen, $expect) as $surprising => $ignored) {
 
178
      $message = $message_map[$surprising];
 
179
      $message_info = $message->getDescription();
 
180
 
 
181
      list($sev, $line, $char) = explode(':', $surprising);
 
182
      $this->assertFailure(
 
183
        "In '{$file}', ".
 
184
        "lint raised {$sev} on line {$line} at char {$char}, ".
 
185
        "but nothing was expected:\n\n{$message_info}\n\n{$raised}");
 
186
    }
 
187
  }
 
188
 
 
189
  private function compareTransform($expected, $actual) {
 
190
    if (!strlen($expected)) {
 
191
      return;
 
192
    }
 
193
    $this->assertEqual(
 
194
      $expected,
 
195
      $actual,
 
196
      'File as patched by lint did not match the expected patched file.');
 
197
  }
 
198
 
 
199
}