1
0
Fork 0
mirror of https://we.phorge.it/source/arcanist.git synced 2024-11-08 07:52:39 +01:00

Promote 2024.19 to stable

This commit is contained in:
Aviv Eyal 2024-06-21 12:02:43 +03:00
commit 1eee3da56e
14 changed files with 67 additions and 126 deletions

View file

@ -275,7 +275,7 @@ final class Filesystem extends Phobject {
$trap->destroy(); $trap->destroy();
if (!$ok) { if (!$ok) {
if (strlen($err)) { if ($err !== null && strlen($err)) {
throw new FilesystemException( throw new FilesystemException(
$to, $to,
pht( pht(
@ -307,7 +307,7 @@ final class Filesystem extends Phobject {
* @task file * @task file
*/ */
public static function remove($path) { public static function remove($path) {
if (!strlen($path)) { if ($path == null || !strlen($path)) {
// Avoid removing PWD. // Avoid removing PWD.
throw new Exception( throw new Exception(
pht( pht(
@ -673,7 +673,7 @@ final class Filesystem extends Phobject {
} }
// If we come back with an encoding, strip it off. // If we come back with an encoding, strip it off.
if (strpos($mime_type, ';') !== false) { if ($mime_type !== null && strpos($mime_type, ';') !== false) {
list($type, $encoding) = explode(';', $mime_type, 2); list($type, $encoding) = explode(';', $mime_type, 2);
$mime_type = $type; $mime_type = $type;
} }

View file

@ -21,7 +21,8 @@ final class HTTPFutureHTTPResponseStatus extends HTTPFutureResponseStatus {
$content_type = BaseHTTPFuture::getHeader($headers, 'Content-Type'); $content_type = BaseHTTPFuture::getHeader($headers, 'Content-Type');
$match = null; $match = null;
if (preg_match('/;\s*charset=([^;]+)/', $content_type, $match)) { if (phutil_nonempty_string($content_type) &&
preg_match('/;\s*charset=([^;]+)/', $content_type, $match)) {
$encoding = trim($match[1], "\"'"); $encoding = trim($match[1], "\"'");
try { try {
$excerpt = phutil_utf8_convert($excerpt, 'UTF-8', $encoding); $excerpt = phutil_utf8_convert($excerpt, 'UTF-8', $encoding);

View file

@ -18,7 +18,8 @@ final class PhutilMissingSymbolException extends Exception {
'moved, your library map may need to be rebuilt. You can rebuild '. 'moved, your library map may need to be rebuilt. You can rebuild '.
'the map by running "arc liberate".'. 'the map by running "arc liberate".'.
"\n\n". "\n\n".
'For more information, see: https://phurl.io/u/newclasses', 'For more information, see: '.
'https://we.phorge.it/book/contrib/article/adding_new_classes/',
$symbol, $symbol,
$type, $type,
$reason); $reason);

View file

@ -332,11 +332,16 @@ final class PhutilLibraryMapBuilder extends Phobject {
'xmap' => array(), 'xmap' => array(),
); );
$type_translation = array(
'interface' => 'class',
'trait' => 'class',
);
// Detect duplicate symbols within the library. // Detect duplicate symbols within the library.
foreach ($symbol_map as $file => $info) { foreach ($symbol_map as $file => $info) {
foreach ($info['have'] as $type => $symbols) { foreach ($info['have'] as $type => $symbols) {
foreach ($symbols as $symbol => $declaration) { foreach ($symbols as $symbol => $declaration) {
$lib_type = ($type == 'interface') ? 'class' : $type; $lib_type = idx($type_translation, $type, $type);
if (!empty($library_map[$lib_type][$symbol])) { if (!empty($library_map[$lib_type][$symbol])) {
$prior = $library_map[$lib_type][$symbol]; $prior = $library_map[$lib_type][$symbol];
throw new Exception( throw new Exception(

View file

@ -187,6 +187,11 @@ final class ArcanistDiffParser extends Phobject {
} }
public function parseDiff($diff) { public function parseDiff($diff) {
// Remove leading UTF-8 Byte Order Mark (BOM)
if (substr($diff, 0, 3) == pack('CCC', 0xEF, 0xBB, 0xBF)) {
$diff = substr($diff, 3);
}
if (!strlen(trim($diff))) { if (!strlen(trim($diff))) {
throw new Exception(pht("Can't parse an empty diff!")); throw new Exception(pht("Can't parse an empty diff!"));
} }

View file

@ -188,7 +188,7 @@ final class ArcanistRuntime {
tsprintf( tsprintf(
'%s <__%s__>', '%s <__%s__>',
pht('Learn More:'), pht('Learn More:'),
'https://phurl.io/u/noninteractive')); 'https://secure.phabricator.com/T13491'));
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(
pht('Missing required "--" in argument list.')); pht('Missing required "--" in argument list.'));

View file

@ -398,6 +398,12 @@ final class PhutilSymbolLoader {
} }
private static function classLikeExists($name) {
return class_exists($name, false) ||
interface_exists($name, false) ||
trait_exists($name, false);
}
/** /**
* @task internal * @task internal
*/ */
@ -411,7 +417,7 @@ final class PhutilSymbolLoader {
return; return;
} }
} else { } else {
if (class_exists($name, false) || interface_exists($name, false)) { if (self::classLikeExists($name)) {
return; return;
} }
} }
@ -431,7 +437,7 @@ final class PhutilSymbolLoader {
$load_failed = pht('function'); $load_failed = pht('function');
} }
} else { } else {
if (!class_exists($name, false) && !interface_exists($name, false)) { if (!self::classLikeExists($name)) {
$load_failed = pht('class or interface'); $load_failed = pht('class or interface');
} }
} }

View file

@ -52,7 +52,7 @@ final class PhpunitTestEngine extends ArcanistUnitTestEngine {
if (!Filesystem::pathExists($test_path)) { if (!Filesystem::pathExists($test_path)) {
continue; continue;
} }
$json_tmp = new TempFile(); $xml_tmp = new TempFile();
$clover_tmp = null; $clover_tmp = null;
$clover = null; $clover = null;
if ($this->getEnableCoverage() !== false) { if ($this->getEnableCoverage() !== false) {
@ -64,10 +64,10 @@ final class PhpunitTestEngine extends ArcanistUnitTestEngine {
$stderr = '-d display_errors=stderr'; $stderr = '-d display_errors=stderr';
$futures[$test_path] = new ExecFuture('%C %C %C --log-json %s %C %s', $futures[$test_path] = new ExecFuture('%C %C %C --log-junit %s %C %s',
$this->phpunitBinary, $config, $stderr, $json_tmp, $clover, $test_path); $this->phpunitBinary, $config, $stderr, $xml_tmp, $clover, $test_path);
$tmpfiles[$test_path] = array( $tmpfiles[$test_path] = array(
'json' => $json_tmp, 'xml' => $xml_tmp,
'clover' => $clover_tmp, 'clover' => $clover_tmp,
); );
} }
@ -81,7 +81,7 @@ final class PhpunitTestEngine extends ArcanistUnitTestEngine {
$results[] = $this->parseTestResults( $results[] = $this->parseTestResults(
$test, $test,
$tmpfiles[$test]['json'], $tmpfiles[$test]['xml'],
$tmpfiles[$test]['clover'], $tmpfiles[$test]['clover'],
$stderr); $stderr);
} }
@ -99,8 +99,8 @@ final class PhpunitTestEngine extends ArcanistUnitTestEngine {
* *
* @return array * @return array
*/ */
private function parseTestResults($path, $json_tmp, $clover_tmp, $stderr) { private function parseTestResults($path, $xml_tmp, $clover_tmp, $stderr) {
$test_results = Filesystem::readFile($json_tmp); $test_results = Filesystem::readFile($xml_tmp);
return id(new ArcanistPhpunitTestResultParser()) return id(new ArcanistPhpunitTestResultParser())
->setEnableCoverage($this->getEnableCoverage()) ->setEnableCoverage($this->getEnableCoverage())
->setProjectRoot($this->projectRoot) ->setProjectRoot($this->projectRoot)

View file

@ -25,80 +25,19 @@ final class ArcanistPhpunitTestResultParser extends ArcanistTestResultParser {
return array($result); return array($result);
} }
$report = $this->getJsonReport($test_results);
// coverage is for all testcases in the executed $path // coverage is for all testcases in the executed $path
$coverage = array(); $coverage = array();
if ($this->enableCoverage !== false) { if ($this->enableCoverage !== false) {
$coverage = $this->readCoverage(); $coverage = $this->readCoverage();
} }
$last_test_finished = true; $xunit_result_parser = new ArcanistXUnitTestResultParser();
$results = $xunit_result_parser->parseTestResults($test_results);
$results = array(); foreach ($results as $result) {
foreach ($report as $event) {
switch (idx($event, 'event')) {
case 'test':
break;
case 'testStart':
$last_test_finished = false;
// fall through
default:
continue 2; // switch + loop
}
$status = ArcanistUnitTestResult::RESULT_PASS;
$user_data = '';
if ('fail' == idx($event, 'status')) {
$status = ArcanistUnitTestResult::RESULT_FAIL;
$user_data .= idx($event, 'message')."\n";
foreach (idx($event, 'trace') as $trace) {
$user_data .= sprintf(
"\n%s:%s",
idx($trace, 'file'),
idx($trace, 'line'));
}
} else if ('error' == idx($event, 'status')) {
if (strpos(idx($event, 'message'), 'Skipped Test') !== false) {
$status = ArcanistUnitTestResult::RESULT_SKIP;
$user_data .= idx($event, 'message');
} else if (strpos(
idx($event, 'message'),
'Incomplete Test') !== false) {
$status = ArcanistUnitTestResult::RESULT_SKIP;
$user_data .= idx($event, 'message');
} else {
$status = ArcanistUnitTestResult::RESULT_BROKEN;
$user_data .= idx($event, 'message');
foreach (idx($event, 'trace') as $trace) {
$user_data .= sprintf(
"\n%s:%s",
idx($trace, 'file'),
idx($trace, 'line'));
}
}
}
$name = preg_replace('/ \(.*\)/s', '', idx($event, 'test'));
$result = new ArcanistUnitTestResult();
$result->setName($name);
$result->setResult($status);
$result->setDuration(idx($event, 'time'));
$result->setCoverage($coverage); $result->setCoverage($coverage);
$result->setUserData($user_data);
$results[] = $result;
$last_test_finished = true;
} }
if (!$last_test_finished) {
$results[] = id(new ArcanistUnitTestResult())
->setName(idx($event, 'test')) // use last event
->setUserData($this->stderr)
->setResult(ArcanistUnitTestResult::RESULT_BROKEN);
}
return $results; return $results;
} }
@ -161,28 +100,4 @@ final class ArcanistPhpunitTestResultParser extends ArcanistTestResultParser {
return $reports; return $reports;
} }
/**
* We need this non-sense to make json generated by phpunit
* valid.
*
* @param string $json String containing JSON report
* @return array JSON decoded array
*/
private function getJsonReport($json) {
if (empty($json)) {
throw new Exception(
pht(
'JSON report file is empty, it probably means that phpunit '.
'failed to run tests. Try running %s with %s option and then run '.
'generated phpunit command yourself, you might get the answer.',
'arc unit',
'--trace'));
}
$json = preg_replace('/}{\s*"/', '},{"', $json);
$json = '['.$json.']';
return phutil_json_decode($json);
}
} }

View file

@ -30,7 +30,7 @@ final class PhutilSortVector
} }
public function addString($value) { public function addString($value) {
if (strlen($value) && (strpos("\0", $value) !== false)) { if (strlen($value) && (strpos($value, "\0") !== false)) {
throw new Exception( throw new Exception(
pht( pht(
'String components of a sort vector must not contain NULL bytes.')); 'String components of a sort vector must not contain NULL bytes.'));

View file

@ -13,8 +13,9 @@
* *
* id(new Thing())->doStuff(); * id(new Thing())->doStuff();
* *
* @param wild Anything. * @template T
* @return wild Unmodified argument. * @param T $x Anything
* @return T Unmodified argument.
*/ */
function id($x) { function id($x) {
return $x; return $x;

View file

@ -1516,7 +1516,14 @@ abstract class ArcanistWorkflow extends Phobject {
} }
} }
/**
* @param string|null $revision_id
* @return string
*/
final protected function normalizeRevisionID($revision_id) { final protected function normalizeRevisionID($revision_id) {
if ($revision_id === null) {
return '';
}
return preg_replace('/^D/i', '', $revision_id); return preg_replace('/^D/i', '', $revision_id);
} }

View file

@ -289,7 +289,7 @@ final class ArcanistWorkingCopyIdentity extends Phobject {
} }
public function readLocalArcConfig() { public function readLocalArcConfig() {
if (strlen($this->localMetaDir)) { if ($this->localMetaDir !== null && strlen($this->localMetaDir)) {
$local_path = Filesystem::resolvePath('arc/config', $this->localMetaDir); $local_path = Filesystem::resolvePath('arc/config', $this->localMetaDir);
$console = PhutilConsole::getConsole(); $console = PhutilConsole::getConsole();

View file

@ -112,18 +112,6 @@ foreach ($namespaces as $namespace) {
$namespace, $path, pht('namespace `%s` statements', 'use')); $namespace, $path, pht('namespace `%s` statements', 'use'));
} }
$possible_traits = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
foreach ($possible_traits as $possible_trait) {
$attributes = $possible_trait->getChildByIndex(0);
// Can't use getChildByIndex here because not all classes have attributes
foreach ($attributes->getChildren() as $attribute) {
if (strtolower($attribute->getConcreteString()) === 'trait') {
phutil_fail_on_unsupported_feature($possible_trait, $path, pht('traits'));
}
}
}
// -( Marked Externals )------------------------------------------------------ // -( Marked Externals )------------------------------------------------------
@ -256,17 +244,29 @@ foreach ($calls as $call) {
// Find classes declared by this file. // Find classes declared by this file.
// This is "class X ... { ... }". // This is "class X ... { ... }".
$classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION'); function build_have_element_for_class_declaration(XHPASTNode $class_node) {
foreach ($classes as $class) { $class_name = $class_node->getChildByIndex(1);
$class_name = $class->getChildByIndex(1);
$have[] = array( $type = 'class';
'type' => 'class', $attributes = $class_node->getChildByIndex(0);
foreach ($attributes->getChildren() as $attribute) {
if (strtolower($attribute->getConcreteString()) === 'trait') {
$type = 'trait';
}
}
return array(
'type' => $type,
'symbol' => $class_name, 'symbol' => $class_name,
); );
} }
$classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION');
foreach ($classes as $class) {
$have[] = build_have_element_for_class_declaration($class);
}
// Find classes used by this file. We identify these: // Find classes used by this file. We identify these:
// //