Changeset View
Changeset View
Standalone View
Standalone View
src/lint/linter/__tests__/ArcanistLinterTestCase.php
Show First 20 Lines • Show All 58 Lines • ▼ Show 20 Lines | if (count($contents) < 2) { | ||||
"Expected '%s' separating test case and results.", | "Expected '%s' separating test case and results.", | ||||
'~~~~~~~~~~')); | '~~~~~~~~~~')); | ||||
} | } | ||||
list($data, $expect, $xform, $config) = array_merge( | list($data, $expect, $xform, $config) = array_merge( | ||||
$contents, | $contents, | ||||
array(null, null)); | array(null, null)); | ||||
$basename = basename($file); | |||||
if ($config) { | if ($config) { | ||||
$config = phutil_json_decode($config); | $config = phutil_json_decode($config); | ||||
} else { | } else { | ||||
$config = array(); | $config = array(); | ||||
} | } | ||||
PhutilTypeSpec::checkMap( | PhutilTypeSpec::checkMap( | ||||
$config, | $config, | ||||
array( | array( | ||||
'config' => 'optional map<string, wild>', | 'config' => 'optional map<string, wild>', | ||||
'mode' => 'optional string', | 'mode' => 'optional string', | ||||
'path' => 'optional string', | 'path' => 'optional string', | ||||
'stopped' => 'optional bool', | 'stopped' => 'optional bool', | ||||
)); | )); | ||||
$exception = null; | $exception = null; | ||||
$after_lint = null; | $after_lint = null; | ||||
$messages = null; | $messages = null; | ||||
$exception_message = false; | $exception_message = false; | ||||
$caught_exception = false; | $caught_exception = false; | ||||
try { | try { | ||||
$path_name = idx($config, 'path'); | |||||
if ($path_name !== null) { | |||||
$basename = basename($path_name); | |||||
} else { | |||||
$basename = basename($file); | |||||
} | |||||
$tmp = new TempFile($basename); | $tmp = new TempFile($basename); | ||||
Filesystem::writeFile($tmp, $data); | Filesystem::writeFile($tmp, $data); | ||||
$full_path = (string)$tmp; | $full_path = (string)$tmp; | ||||
$mode = idx($config, 'mode'); | $mode = idx($config, 'mode'); | ||||
if ($mode) { | if ($mode) { | ||||
Filesystem::changePermissions($tmp, octdec($mode)); | Filesystem::changePermissions($tmp, octdec($mode)); | ||||
} | } | ||||
$dir = dirname($full_path); | $dir = dirname($full_path); | ||||
$path = basename($full_path); | |||||
$working_copy = ArcanistWorkingCopyIdentity::newFromRootAndConfigFile( | $working_copy = ArcanistWorkingCopyIdentity::newFromRootAndConfigFile( | ||||
$dir, | $dir, | ||||
null, | null, | ||||
pht('Unit Test')); | pht('Unit Test')); | ||||
$configuration_manager = new ArcanistConfigurationManager(); | $configuration_manager = new ArcanistConfigurationManager(); | ||||
$configuration_manager->setWorkingCopyIdentity($working_copy); | $configuration_manager->setWorkingCopyIdentity($working_copy); | ||||
$engine = new ArcanistUnitTestableLintEngine(); | $engine = new ArcanistUnitTestableLintEngine(); | ||||
$engine->setWorkingCopy($working_copy); | $engine->setWorkingCopy($working_copy); | ||||
$engine->setConfigurationManager($configuration_manager); | $engine->setConfigurationManager($configuration_manager); | ||||
$path_name = idx($config, 'path', $path); | $engine->setPaths(array($basename)); | ||||
$engine->setPaths(array($path_name)); | |||||
$linter->setEngine($engine); | $linter->setEngine($engine); | ||||
$linter->addPath($path_name); | $linter->addPath($basename); | ||||
$linter->addData($path_name, $data); | $linter->addData($basename, $data); | ||||
foreach (idx($config, 'config', array()) as $key => $value) { | foreach (idx($config, 'config', array()) as $key => $value) { | ||||
$linter->setLinterConfigurationValue($key, $value); | $linter->setLinterConfigurationValue($key, $value); | ||||
} | } | ||||
$engine->addLinter($linter); | $engine->addLinter($linter); | ||||
$engine->addFileData($path_name, $data); | $engine->addFileData($basename, $data); | ||||
$results = $engine->run(); | $results = $engine->run(); | ||||
$this->assertEqual( | $this->assertEqual( | ||||
1, | 1, | ||||
count($results), | count($results), | ||||
pht('Expect one result returned by linter.')); | pht('Expect one result returned by linter.')); | ||||
$assert_stopped = idx($config, 'stopped'); | $assert_stopped = idx($config, 'stopped'); | ||||
if ($assert_stopped !== null) { | if ($assert_stopped !== null) { | ||||
$this->assertEqual( | $this->assertEqual( | ||||
▲ Show 20 Lines • Show All 45 Lines • ▼ Show 20 Lines | private function compareLint($file, $expect, ArcanistLintResult $results) { | ||||
} | } | ||||
foreach ($expect as $result) { | foreach ($expect as $result) { | ||||
$parts = explode(':', $result); | $parts = explode(':', $result); | ||||
$message = new ArcanistLintMessage(); | $message = new ArcanistLintMessage(); | ||||
$severity = idx($parts, 0); | $severity = idx($parts, 0); | ||||
$line = idx($parts, 1); | $line = idx($parts, 1); | ||||
if ($line === '') { | |||||
$line = null; | |||||
} | |||||
$char = idx($parts, 2); | $char = idx($parts, 2); | ||||
if ($char === '') { | |||||
$char = null; | |||||
} | |||||
$code = idx($parts, 3); | $code = idx($parts, 3); | ||||
if ($code === '') { | |||||
$code = null; | |||||
} | |||||
if ($severity !== null) { | if ($severity !== null) { | ||||
$message->setSeverity($severity); | $message->setSeverity($severity); | ||||
} | } | ||||
if ($line !== null) { | if ($line !== null) { | ||||
$message->setLine($line); | $message->setLine($line); | ||||
} | } | ||||
▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | foreach ($expected_results->getMessages() as $expected_message) { | ||||
} | } | ||||
if (!$found) { | if (!$found) { | ||||
$missing[] = $expected_message; | $missing[] = $expected_message; | ||||
} | } | ||||
} | } | ||||
if ($missing || $surprising) { | if ($missing || $surprising) { | ||||
$expected = pht('EXPECTED MESSAGES'); | |||||
if ($missing) { | |||||
foreach ($missing as $message) { | |||||
$expected .= sprintf( | |||||
"\n %s: %s %s", | |||||
pht( | |||||
'%s at line %d, char %d', | |||||
$message->getSeverity(), | |||||
$message->getLine(), | |||||
$message->getChar()), | |||||
$message->getCode(), | |||||
$message->getName()); | |||||
} | |||||
} else { | |||||
$expected .= "\n ".pht('No messages'); | |||||
} | |||||
$actual = pht('UNEXPECTED MESSAGES'); | |||||
if ($surprising) { | |||||
foreach ($surprising as $message) { | |||||
$actual .= sprintf( | |||||
"\n %s: %s %s", | |||||
pht( | |||||
'%s at line %d, char %d', | |||||
$message->getSeverity(), | |||||
$message->getLine(), | |||||
$message->getChar()), | |||||
$message->getCode(), | |||||
$message->getName()); | |||||
} | |||||
} else { | |||||
$actual .= "\n ".pht('No messages'); | |||||
} | |||||
$this->assertFailure( | $this->assertFailure( | ||||
sprintf( | sprintf( | ||||
"%s\n\n%s\n\n%s", | "%s\n%s%s", | ||||
pht("Lint failed for '%s'.", $file), | pht( | ||||
$expected, | 'Lint emitted an unexpected set of messages for file "%s".', | ||||
$actual)); | $file), | ||||
$this->renderMessages(pht('MISSING MESSAGES'), $missing), | |||||
$this->renderMessages(pht('SURPLUS MESSAGES'), $surprising))); | |||||
} | } | ||||
} | } | ||||
private function compareTransform($expected, $actual) { | private function compareTransform($expected, $actual) { | ||||
if (!strlen($expected)) { | if (!strlen($expected)) { | ||||
return; | return; | ||||
} | } | ||||
$this->assertEqual( | $this->assertEqual( | ||||
$expected, | $expected, | ||||
$actual, | $actual, | ||||
pht('File as patched by lint did not match the expected patched file.')); | pht('File as patched by lint did not match the expected patched file.')); | ||||
} | } | ||||
/** | /** | ||||
* Compare properties of @{class:ArcanistLintMessage} instances. | * Compare properties of @{class:ArcanistLintMessage} instances. | ||||
* | * | ||||
* The expectation is that if one (or both) of the properties is null, then | |||||
* we don't care about its value. | |||||
* | |||||
* @param wild | * @param wild | ||||
* @param wild | * @param wild | ||||
* @return bool | * @return bool | ||||
*/ | */ | ||||
private static function compareLintMessageProperty($x, $y) { | private static function compareLintMessageProperty($x, $y) { | ||||
return $x === null || $y === null || $x === $y; | if ($x === null) { | ||||
return true; | |||||
} | |||||
return ($x === $y); | |||||
} | |||||
private function renderMessages($header, array $messages) { | |||||
if (!$messages) { | |||||
$display = tsprintf( | |||||
"%s\n", | |||||
pht('(No messages.)')); | |||||
} else { | |||||
$lines = array(); | |||||
foreach ($messages as $message) { | |||||
$line = $message->getLine(); | |||||
if ($line === null) { | |||||
$display_line = pht('<null>'); | |||||
} else { | |||||
$display_line = $line; | |||||
} | |||||
$char = $message->getChar(); | |||||
if ($char === null) { | |||||
$display_char = pht('<null>'); | |||||
} else { | |||||
$display_char = $char; | |||||
} | |||||
$code = $message->getCode(); | |||||
$name = $message->getName(); | |||||
if ($code !== null && $name !== null) { | |||||
$display_code = pht('%s: %s', $code, $name); | |||||
} else if ($code !== null) { | |||||
$display_code = pht('%s', $code); | |||||
} else { | |||||
$display_code = null; | |||||
} | |||||
$severity = $message->getSeverity(); | |||||
if ($display_code === null) { | |||||
$display_message = pht( | |||||
'Message with severity "%s" at "%s:%s"', | |||||
$severity, | |||||
$display_line, | |||||
$display_char); | |||||
} else { | |||||
$display_message = pht( | |||||
'Message with severity "%s" at "%s:%s" (%s)', | |||||
$severity, | |||||
$display_line, | |||||
$display_char, | |||||
$display_code); | |||||
} | |||||
$lines[] = tsprintf( | |||||
" %s\n", | |||||
$display_message); | |||||
} | |||||
$display = implode('', $lines); | |||||
} | |||||
return tsprintf( | |||||
"%s\n%B\n", | |||||
$header, | |||||
$display); | |||||
} | } | ||||
} | } |