public static function loadOneSkinSpecification($name)
 {
     // Only allow skins which we know to exist to load. This prevents loading
     // skins like "../../secrets/evil/".
     $all = self::loadAllSkinSpecifications();
     if (empty($all[$name])) {
         throw new Exception(pht('Blog skin "%s" is not a valid skin!', $name));
     }
     $paths = PhabricatorEnv::getEnvConfig('phame.skins');
     $base = dirname(phutil_get_library_root('phabricator'));
     foreach ($paths as $path) {
         $path = Filesystem::resolvePath($path, $base);
         $skin_path = $path . DIRECTORY_SEPARATOR . $name;
         if (is_dir($skin_path)) {
             // Double check that the skin really lives in the skin directory.
             if (!Filesystem::isDescendant($skin_path, $path)) {
                 throw new Exception(pht('Blog skin "%s" is not located in path "%s"!', $name, $path));
             }
             $spec = self::loadSkinSpecification($skin_path);
             if ($spec) {
                 $spec->setName($name);
                 return $spec;
             }
         }
     }
     return null;
 }
 protected function deleteDocumentsByHash(array $hashes)
 {
     $root = $this->getConfig('root');
     $cache = $this->getPublishCache();
     foreach ($hashes as $hash) {
         $paths = $cache->getAtomPathsFromCache($hash);
         foreach ($paths as $path) {
             $abs = $root . DIRECTORY_SEPARATOR . $path;
             Filesystem::remove($abs);
             // If the parent directory is now empty, clean it up.
             $dir = dirname($abs);
             while (true) {
                 if (!Filesystem::isDescendant($dir, $root)) {
                     // Directory is outside of the root.
                     break;
                 }
                 if (Filesystem::listDirectory($dir)) {
                     // Directory is not empty.
                     break;
                 }
                 Filesystem::remove($dir);
                 $dir = dirname($dir);
             }
         }
         $cache->removeAtomPathsFromCache($hash);
         $cache->deleteAtomFromIndex($hash);
     }
 }
 public function testisDescendant()
 {
     $test_cases = array(array(__FILE__, dirname(__FILE__), true), array(dirname(__FILE__), dirname(dirname(__FILE__)), true), array(dirname(__FILE__), phutil_get_library_root_for_path(__FILE__), true), array(dirname(dirname(__FILE__)), dirname(__FILE__), false), array(dirname(__FILE__) . '/quack', dirname(__FILE__), false));
     foreach ($test_cases as $test_case) {
         list($path, $root, $expected) = $test_case;
         $this->assertEqual($expected, Filesystem::isDescendant($path, $root), sprintf('Filesystem::isDescendant(%s, %s)', phutil_var_export($path), phutil_var_export($root)));
     }
 }
 public function render()
 {
     $user = $this->getUser();
     $trace = $this->trace;
     $libraries = PhutilBootloader::getInstance()->getAllLibraries();
     // TODO: Make this configurable?
     $path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/';
     $callsigns = array('arcanist' => 'ARC', 'phutil' => 'PHU', 'phabricator' => 'P');
     $rows = array();
     $depth = count($trace);
     foreach ($trace as $part) {
         $lib = null;
         $file = idx($part, 'file');
         $relative = $file;
         foreach ($libraries as $library) {
             $root = phutil_get_library_root($library);
             if (Filesystem::isDescendant($file, $root)) {
                 $lib = $library;
                 $relative = Filesystem::readablePath($file, $root);
                 break;
             }
         }
         $where = '';
         if (isset($part['class'])) {
             $where .= $part['class'] . '::';
         }
         if (isset($part['function'])) {
             $where .= $part['function'] . '()';
         }
         if ($file) {
             if (isset($callsigns[$lib])) {
                 $attrs = array('title' => $file);
                 try {
                     $attrs['href'] = $user->loadEditorLink('/src/' . $relative, $part['line'], $callsigns[$lib]);
                 } catch (Exception $ex) {
                     // The database can be inaccessible.
                 }
                 if (empty($attrs['href'])) {
                     $attrs['href'] = sprintf($path, $callsigns[$lib]) . str_replace(DIRECTORY_SEPARATOR, '/', $relative) . '$' . $part['line'];
                     $attrs['target'] = '_blank';
                 }
                 $file_name = phutil_tag('a', $attrs, $relative);
             } else {
                 $file_name = phutil_tag('span', array('title' => $file), $relative);
             }
             $file_name = hsprintf('%s : %d', $file_name, $part['line']);
         } else {
             $file_name = phutil_tag('em', array(), '(Internal)');
         }
         $rows[] = array($depth--, $lib, $file_name, $where);
     }
     $table = new AphrontTableView($rows);
     $table->setHeaders(array(pht('Depth'), pht('Library'), pht('File'), pht('Where')));
     $table->setColumnClasses(array('n', '', '', 'wide'));
     return phutil_tag('div', array('class' => 'exception-trace'), $table->render());
 }
Esempio n. 5
0
 public function readCoverage($cover_file)
 {
     $coverage_dom = new DOMDocument();
     $coverage_dom->loadXML(Filesystem::readFile($cover_file));
     $modified = $this->getPaths();
     $reports = array();
     $classes = $coverage_dom->getElementsByTagName("class");
     foreach ($classes as $class) {
         $path = $class->getAttribute("filename");
         $root = $this->getWorkingCopy()->getProjectRoot();
         if (in_array($path, $modified) === false) {
             continue;
         }
         if (!Filesystem::isDescendant($path, $root)) {
             continue;
         }
         // get total line count in file
         $line_count = count(phutil_split_lines(Filesystem::readFile($path)));
         $coverage = "";
         $start_line = 1;
         $lines = $this->unitTestEngineGetLines($class);
         for ($ii = 0; $ii < count($lines); $ii++) {
             $line = $lines[$ii];
             $next_line = $line["number"];
             for ($start_line; $start_line < $next_line; $start_line++) {
                 $coverage .= "N";
             }
             if ($line["hits"] === 0) {
                 $coverage .= "U";
             } else {
                 if ($line["hits"] > 0) {
                     $coverage .= "C";
                 }
             }
             $start_line++;
         }
         if ($start_line < $line_count) {
             foreach (range($start_line, $line_count) as $line_num) {
                 $coverage .= "N";
             }
         }
         $reports[$path] = $coverage;
     }
     return $reports;
 }
 /**
  * Returns true if an application is first-party (developed by Phacility)
  * and false otherwise.
  *
  * @return bool True if this application is developed by Phacility.
  */
 public final function isFirstParty()
 {
     $where = id(new ReflectionClass($this))->getFileName();
     $root = phutil_get_library_root('phabricator');
     if (!Filesystem::isDescendant($where, $root)) {
         return false;
     }
     if (Filesystem::isDescendant($where, $root . '/extensions')) {
         return false;
     }
     return true;
 }
 /**
  * Returns the paths in which we should look for tests to execute.
  *
  * @return list<string>  A list of paths in which to search for test cases.
  */
 public function getTestPaths()
 {
     $root = $this->getWorkingCopy()->getProjectRoot();
     $paths = array();
     foreach ($this->getPaths() as $path) {
         $library_root = phutil_get_library_root_for_path($path);
         if (!$library_root) {
             continue;
         }
         $library_name = phutil_get_library_name_for_root($library_root);
         if (!$library_name) {
             throw new Exception(pht("Attempting to run unit tests on a libphutil library which has " . "not been loaded, at:\n\n" . "    %s\n\n" . "This probably means one of two things:\n\n" . "    - You may need to add this library to %s.\n" . "    - You may be running tests on a copy of libphutil or " . "arcanist using a different copy of libphutil or arcanist. " . "This operation is not supported.\n", $library_root, '.arcconfig.'));
         }
         $path = Filesystem::resolvePath($path, $root);
         $library_path = Filesystem::readablePath($path, $library_root);
         if (!Filesystem::isDescendant($path, $library_root)) {
             // We have encountered some kind of symlink maze -- for instance, $path
             // is some symlink living outside the library that links into some file
             // inside the library. Just ignore these cases, since the affected file
             // does not actually lie within the library.
             continue;
         }
         if (is_file($path) && preg_match('@(?:^|/)__tests__/@', $path)) {
             $paths[$library_name . ':' . $library_path] = array('library' => $library_name, 'path' => $library_path);
             continue;
         }
         foreach (Filesystem::walkToRoot($path, $library_root) as $subpath) {
             if ($subpath == $library_root) {
                 $paths[$library_name . ':.'] = array('library' => $library_name, 'path' => '__tests__/');
             } else {
                 $library_subpath = Filesystem::readablePath($subpath, $library_root);
                 $paths[$library_name . ':' . $library_subpath] = array('library' => $library_name, 'path' => $library_subpath . '/__tests__/');
             }
         }
     }
     return $paths;
 }
Esempio n. 8
0
 /**
  * Search for test cases for a given file in a large number of "reasonable"
  * locations. See @{method:getSearchLocationsForTests} for specifics.
  *
  * TODO: Add support for finding tests in testsuite folders from
  * phpunit.xml configuration.
  *
  * @param   string      PHP file to locate test cases for.
  * @return  string|null Path to test cases, or null.
  */
 private function findTestFile($path)
 {
     $root = $this->projectRoot;
     $path = Filesystem::resolvePath($path, $root);
     $file = basename($path);
     $possible_files = array($file, substr($file, 0, -4) . 'Test.php');
     $search = self::getSearchLocationsForTests($path);
     foreach ($search as $search_path) {
         foreach ($possible_files as $possible_file) {
             $full_path = $search_path . $possible_file;
             if (!Filesystem::pathExists($full_path)) {
                 // If the file doesn't exist, it's clearly a miss.
                 continue;
             }
             if (!Filesystem::isDescendant($full_path, $root)) {
                 // Don't look above the project root.
                 continue;
             }
             if (0 == strcasecmp(Filesystem::resolvePath($full_path), $path)) {
                 // Don't return the original file.
                 continue;
             }
             return $full_path;
         }
     }
     return null;
 }
 private function getTestsForPaths()
 {
     $project_root = $this->getWorkingCopy()->getProjectRoot();
     $look_here = array();
     foreach ($this->getPaths() as $path) {
         $library_root = phutil_get_library_root_for_path($path);
         if (!$library_root) {
             continue;
         }
         $library_name = phutil_get_library_name_for_root($library_root);
         if (!$library_name) {
             throw new Exception("Attempting to run unit tests on a libphutil library which has not " . "been loaded, at:\n\n" . "    {$library_root}\n\n" . "This probably means one of two things:\n\n" . "    - You may need to add this library to .arcconfig.\n" . "    - You may be running tests on a copy of libphutil or arcanist\n" . "      using a different copy of libphutil or arcanist. This\n" . "      operation is not supported.");
         }
         $path = Filesystem::resolvePath($path, $project_root);
         if (!is_dir($path)) {
             $path = dirname($path);
         }
         if ($path == $library_root) {
             $look_here[$library_name . ':.'] = array('library' => $library_name, 'path' => '');
         } else {
             if (!Filesystem::isDescendant($path, $library_root)) {
                 // We have encountered some kind of symlink maze -- for instance, $path
                 // is some symlink living outside the library that links into some file
                 // inside the library. Just ignore these cases, since the affected file
                 // does not actually lie within the library.
                 continue;
             } else {
                 $library_path = Filesystem::readablePath($path, $library_root);
                 do {
                     $look_here[$library_name . ':' . $library_path] = array('library' => $library_name, 'path' => $library_path);
                     $library_path = dirname($library_path);
                 } while ($library_path != '.');
             }
         }
     }
     // Look for any class that extends ArcanistPhutilTestCase inside a
     // __tests__ directory in any parent directory of every affected file.
     //
     // The idea is that "infrastructure/__tests__/" tests defines general tests
     // for all of "infrastructure/", and those tests run for any change in
     // "infrastructure/". However, "infrastructure/concrete/rebar/__tests__/"
     // defines more specific tests that run only when rebar/ (or some
     // subdirectory) changes.
     $run_tests = array();
     foreach ($look_here as $path_info) {
         $library = $path_info['library'];
         $path = $path_info['path'];
         $symbols = id(new PhutilSymbolLoader())->setType('class')->setLibrary($library)->setPathPrefix(($path ? $path . '/' : '') . '__tests__/')->setAncestorClass('ArcanistPhutilTestCase')->setConcreteOnly(true)->selectAndLoadSymbols();
         foreach ($symbols as $symbol) {
             $run_tests[$symbol['name']] = true;
         }
     }
     $run_tests = array_keys($run_tests);
     return $run_tests;
 }
Esempio n. 10
0
 public function runUnitTests()
 {
     // Build any unit tests
     $this->make('build-tests');
     // Now find all the test programs
     $root = $this->getProjectRoot();
     $futures = array();
     $cg_files = array();
     $obj_files = array();
     $coverage = $this->getEnableCoverage();
     $tests = $this->getTestsToRun();
     foreach ($tests as $relname => $test) {
         if ($coverage) {
             $cg = new TempFile();
             $cg_files[$relname] = $cg;
             $obj_files[] = $test;
             $test = 'valgrind --tool=callgrind --collect-jumps=yes ' . '--callgrind-out-file=' . $cg . ' ' . $test;
         }
         $futures[$relname] = new ExecFuture($test);
     }
     $results = array();
     // Use a smaller limit for SunOS because my test VM has crappy
     // timing characteristics and that causes timer.t to fail
     $limit = PHP_OS == 'SunOS' ? 1 : 4;
     foreach (Futures($futures)->limit($limit) as $test => $future) {
         list($err, $stdout, $stderr) = $future->resolve();
         $results[$test] = $this->parseTestResults($test, $err, $stdout, $stderr);
     }
     if ($coverage) {
         // Find all executables, not just the test executables.
         // We need to do this because the test executables may not
         // include all of the possible code (linker decides to omit
         // it from the image) so we see a skewed representation of
         // the source lines.
         $fp = popen("find {$root} -type f -name \\*.a -o -name \\*.so -o -name \\*.dylib", "r");
         while (true) {
             $line = fgets($fp);
             if ($line === false) {
                 break;
             }
             $obj_files[] = trim($line);
         }
         // Parse line information from the objects
         foreach ($obj_files as $object) {
             PhenomDwarfLineInfo::loadObject($object);
         }
         // Now process coverage data files
         foreach ($cg_files as $relname => $cg) {
             $CG = new PhenomCallgrindFile((string) $cg);
             $CG->parse();
             $source_files = array();
             foreach ($CG->getSourceFiles() as $filename) {
                 if (Filesystem::isDescendant($filename, $root) && !preg_match("/(thirdparty|tests)/", $filename)) {
                     $source_files[$filename] = $filename;
                 }
             }
             $cov = array();
             foreach ($source_files as $filename) {
                 $relsrc = substr($filename, strlen($root) + 1);
                 $cov[$relsrc] = PhenomCallgrindFile::mergeSourceLineData($filename, array($CG));
             }
             // Update the coverage data for that test.
             // arcanist will merge the final results
             $results[$relname]->setCoverage($cov);
         }
     }
     return $results;
 }
Esempio n. 11
0
/**
 * NOTE: SPOOKY BLACK MAGIC
 *
 * When arc is run in a copy of arcanist other than itself, or a copy of
 * libphutil other than the one we loaded, reenter the script and force it
 * to use the current working directory instead of the default.
 *
 * In the case of execution inside arcanist/, we force execution of the local
 * arc binary.
 *
 * In the case of execution inside libphutil/, we force the local copy to load
 * instead of the one selected by default rules.
 *
 * @param PhutilConsole                 Console.
 * @param ArcanistWorkingCopyIdentity   The current working copy.
 * @param array                         Original arc arguments.
 * @return void
 */
function reenter_if_this_is_arcanist_or_libphutil(PhutilConsole $console, ArcanistWorkingCopyIdentity $working_copy, array $original_argv)
{
    $project_id = $working_copy->getProjectID();
    if ($project_id != 'arcanist' && $project_id != 'libphutil') {
        // We're not in a copy of arcanist or libphutil.
        return;
    }
    $library_names = array('arcanist' => 'arcanist', 'libphutil' => 'phutil');
    $library_root = phutil_get_library_root($library_names[$project_id]);
    $project_root = $working_copy->getProjectRoot();
    if (Filesystem::isDescendant($library_root, $project_root)) {
        // We're in a copy of arcanist or libphutil, but already loaded the correct
        // copy. Continue execution normally.
        return;
    }
    if ($project_id == 'libphutil') {
        $console->writeLog("This is libphutil! Forcing this copy to load...\n");
        $original_argv[0] = dirname(phutil_get_library_root('arcanist')) . '/bin/arc';
        $libphutil_path = $project_root;
    } else {
        $console->writeLog("This is arcanist! Forcing this copy to run...\n");
        $original_argv[0] = $project_root . '/bin/arc';
        $libphutil_path = dirname(phutil_get_library_root('phutil'));
    }
    if (phutil_is_windows()) {
        $err = phutil_passthru('set ARC_PHUTIL_PATH=%s & %Ls', $libphutil_path, $original_argv);
    } else {
        $err = phutil_passthru('ARC_PHUTIL_PATH=%s %Ls', $libphutil_path, $original_argv);
    }
    exit($err);
}
Esempio n. 12
0
 protected function getCommitFileList(array $revision)
 {
     $repository_api = $this->getRepositoryAPI();
     $revision_id = $revision['id'];
     $commit_paths = $this->getConduit()->callMethodSynchronous('differential.getcommitpaths', array('revision_id' => $revision_id));
     $dir_paths = array();
     foreach ($commit_paths as $path) {
         $path = dirname($path);
         while ($path != '.') {
             $dir_paths[$path] = true;
             $path = dirname($path);
         }
     }
     $commit_paths = array_fill_keys($commit_paths, true);
     $status = $repository_api->getSVNStatus();
     $modified_but_not_included = array();
     foreach ($status as $path => $mask) {
         if (!empty($dir_paths[$path])) {
             $commit_paths[$path] = true;
         }
         if (!empty($commit_paths[$path])) {
             continue;
         }
         foreach ($commit_paths as $will_commit => $ignored) {
             if (Filesystem::isDescendant($path, $will_commit)) {
                 throw new ArcanistUsageException("This commit includes the directory '{$will_commit}', but " . "it contains a modified path ('{$path}') which is NOT included " . "in the commit. Subversion can not handle this operation and " . "will commit the path anyway. You need to sort out the working " . "copy changes to '{$path}' before you may proceed with the " . "commit.");
             }
         }
         $modified_but_not_included[] = $path;
     }
     if ($modified_but_not_included) {
         $prefix = pht('Locally modified path(s) are not included in this revision:', count($modified_but_not_included));
         $prompt = pht('They will NOT be committed. Commit this revision anyway?', count($modified_but_not_included));
         $this->promptFileWarning($prefix, $prompt, $modified_but_not_included);
     }
     $do_not_exist = array();
     foreach ($commit_paths as $path => $ignored) {
         $disk_path = $repository_api->getPath($path);
         if (file_exists($disk_path)) {
             continue;
         }
         if (is_link($disk_path)) {
             continue;
         }
         if (idx($status, $path) & ArcanistRepositoryAPI::FLAG_DELETED) {
             continue;
         }
         $do_not_exist[] = $path;
         unset($commit_paths[$path]);
     }
     if ($do_not_exist) {
         $prefix = pht('Revision includes changes to path(s) that do not exist:', count($do_not_exist));
         $prompt = "Commit this revision anyway?";
         $this->promptFileWarning($prefix, $prompt, $do_not_exist);
     }
     $files = array_keys($commit_paths);
     if (empty($files)) {
         throw new ArcanistUsageException("There is nothing left to commit. None of the modified paths exist.");
     }
     return $files;
 }
 private function renderStackTrace($trace, PhabricatorUser $user)
 {
     $libraries = PhutilBootloader::getInstance()->getAllLibraries();
     $version = PhabricatorEnv::getEnvConfig('phabricator.version');
     if (preg_match('/[^a-f0-9]/i', $version)) {
         $version = '';
     }
     // TODO: Make this configurable?
     $path = 'https://secure.phabricator.com/diffusion/%s/browse/master/src/';
     $callsigns = array('arcanist' => 'ARC', 'phutil' => 'PHU', 'phabricator' => 'P');
     $rows = array();
     $depth = count($trace);
     foreach ($trace as $part) {
         $lib = null;
         $file = idx($part, 'file');
         $relative = $file;
         foreach ($libraries as $library) {
             $root = phutil_get_library_root($library);
             if (Filesystem::isDescendant($file, $root)) {
                 $lib = $library;
                 $relative = Filesystem::readablePath($file, $root);
                 break;
             }
         }
         $where = '';
         if (isset($part['class'])) {
             $where .= $part['class'] . '::';
         }
         if (isset($part['function'])) {
             $where .= $part['function'] . '()';
         }
         if ($file) {
             if (isset($callsigns[$lib])) {
                 $attrs = array('title' => $file);
                 try {
                     $attrs['href'] = $user->loadEditorLink('/src/' . $relative, $part['line'], $callsigns[$lib]);
                 } catch (Exception $ex) {
                     // The database can be inaccessible.
                 }
                 if (empty($attrs['href'])) {
                     $attrs['href'] = sprintf($path, $callsigns[$lib]) . str_replace(DIRECTORY_SEPARATOR, '/', $relative) . ($version && $lib == 'phabricator' ? ';' . $version : '') . '$' . $part['line'];
                     $attrs['target'] = '_blank';
                 }
                 $file_name = phutil_render_tag('a', $attrs, phutil_escape_html($relative));
             } else {
                 $file_name = phutil_render_tag('span', array('title' => $file), phutil_escape_html($relative));
             }
             $file_name = $file_name . ' : ' . (int) $part['line'];
         } else {
             $file_name = '<em>(Internal)</em>';
         }
         $rows[] = array($depth--, phutil_escape_html($lib), $file_name, phutil_escape_html($where));
     }
     $table = new AphrontTableView($rows);
     $table->setHeaders(array('Depth', 'Library', 'File', 'Where'));
     $table->setColumnClasses(array('n', '', '', 'wide'));
     return '<div class="exception-trace">' . '<div class="exception-trace-header">Stack Trace</div>' . $table->render() . '</div>';
 }
Esempio n. 14
0
 public function run()
 {
     $bootloader = PhutilBootloader::getInstance();
     $project_root = $this->getWorkingCopy()->getProjectRoot();
     $look_here = array();
     foreach ($this->getPaths() as $path) {
         $library_root = phutil_get_library_root_for_path($path);
         if (!$library_root) {
             continue;
         }
         $library_name = phutil_get_library_name_for_root($library_root);
         if (!$library_name) {
             throw new Exception("Attempting to run unit tests on a libphutil library which has not " . "been loaded, at:\n\n" . "    {$library_root}\n\n" . "This probably means one of two things:\n\n" . "    - You may need to add this library to .arcconfig.\n" . "    - You may be running tests on a copy of libphutil or arcanist\n" . "      using a different copy of libphutil or arcanist. This\n" . "      operation is not supported.");
         }
         $path = Filesystem::resolvePath($path, $project_root);
         if (!is_dir($path)) {
             $path = dirname($path);
         }
         if ($path == $library_root) {
             continue;
         }
         if (!Filesystem::isDescendant($path, $library_root)) {
             // We have encountered some kind of symlink maze -- for instance, $path
             // is some symlink living outside the library that links into some file
             // inside the library. Just ignore these cases, since the affected file
             // does not actually lie within the library.
             continue;
         }
         $library_path = Filesystem::readablePath($path, $library_root);
         do {
             $look_here[$library_name . ':' . $library_path] = array('library' => $library_name, 'path' => $library_path);
             $library_path = dirname($library_path);
         } while ($library_path != '.');
     }
     // Look for any class that extends ArcanistPhutilTestCase inside a
     // __tests__ directory in any parent directory of every affected file.
     //
     // The idea is that "infrastructure/__tests__/" tests defines general tests
     // for all of "infrastructure/", and those tests run for any change in
     // "infrastructure/". However, "infrastructure/concrete/rebar/__tests__/"
     // defines more specific tests that run only when rebar/ (or some
     // subdirectory) changes.
     $run_tests = array();
     foreach ($look_here as $path_info) {
         $library = $path_info['library'];
         $path = $path_info['path'];
         $symbols = id(new PhutilSymbolLoader())->setType('class')->setLibrary($library)->setPathPrefix($path . '/__tests__/')->setAncestorClass('ArcanistPhutilTestCase')->setConcreteOnly(true)->selectAndLoadSymbols();
         foreach ($symbols as $symbol) {
             $run_tests[$symbol['name']] = true;
         }
     }
     $run_tests = array_keys($run_tests);
     if (!$run_tests) {
         throw new ArcanistNoEffectException("No tests to run.");
     }
     $enable_coverage = $this->getEnableCoverage();
     if ($enable_coverage !== false) {
         if (!function_exists('xdebug_start_code_coverage')) {
             if ($enable_coverage === true) {
                 throw new ArcanistUsageException("You specified --coverage but xdebug is not available, so " . "coverage can not be enabled for PhutilUnitTestEngine.");
             }
         } else {
             $enable_coverage = true;
         }
     }
     $results = array();
     foreach ($run_tests as $test_class) {
         $test_case = newv($test_class, array());
         $test_case->setEnableCoverage($enable_coverage);
         $test_case->setProjectRoot($project_root);
         $test_case->setPaths($this->getPaths());
         $results[] = $test_case->run();
     }
     if ($results) {
         $results = call_user_func_array('array_merge', $results);
     }
     return $results;
 }
Esempio n. 15
0
 public function readCoverage($cover_file, $source_path)
 {
     $coverage_dom = new DOMDocument();
     $coverage_dom->loadXML(Filesystem::readFile($cover_file));
     $reports = array();
     $classes = $coverage_dom->getElementsByTagName('class');
     foreach ($classes as $class) {
         $path = $class->getAttribute('filename');
         $root = $this->getWorkingCopy()->getProjectRoot();
         if (!Filesystem::isDescendant($path, $root)) {
             continue;
         }
         // get total line count in file
         $line_count = count(phutil_split_lines(Filesystem::readFile($path)));
         $coverage = '';
         $start_line = 1;
         $lines = $class->getElementsByTagName('line');
         for ($ii = 0; $ii < $lines->length; $ii++) {
             $line = $lines->item($ii);
             $next_line = intval($line->getAttribute('number'));
             for ($start_line; $start_line < $next_line; $start_line++) {
                 $coverage .= 'N';
             }
             if (intval($line->getAttribute('hits')) == 0) {
                 $coverage .= 'U';
             } else {
                 if (intval($line->getAttribute('hits')) > 0) {
                     $coverage .= 'C';
                 }
             }
             $start_line++;
         }
         if ($start_line < $line_count) {
             foreach (range($start_line, $line_count) as $line_num) {
                 $coverage .= 'N';
             }
         }
         $reports[$path] = $coverage;
     }
     return $reports;
 }
 private function buildRepositoryStatus(PhabricatorRepository $repository)
 {
     $viewer = $this->getRequest()->getUser();
     $is_cluster = $repository->getAlmanacServicePHID();
     $view = new PHUIStatusListView();
     $messages = id(new PhabricatorRepositoryStatusMessage())->loadAllWhere('repositoryID = %d', $repository->getID());
     $messages = mpull($messages, null, 'getStatusType');
     if ($repository->isTracked()) {
         $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Repository Active')));
     } else {
         $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'bluegrey')->setTarget(pht('Repository Inactive'))->setNote(pht('Activate this repository to begin or resume import.')));
         return $view;
     }
     $binaries = array();
     $svnlook_check = false;
     switch ($repository->getVersionControlSystem()) {
         case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
             $binaries[] = 'git';
             break;
         case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
             $binaries[] = 'svn';
             break;
         case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
             $binaries[] = 'hg';
             break;
     }
     if ($repository->isHosted()) {
         if ($repository->getServeOverHTTP() != PhabricatorRepository::SERVE_OFF) {
             switch ($repository->getVersionControlSystem()) {
                 case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
                     $binaries[] = 'git-http-backend';
                     break;
                 case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
                     $binaries[] = 'svnserve';
                     $binaries[] = 'svnadmin';
                     $binaries[] = 'svnlook';
                     $svnlook_check = true;
                     break;
                 case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
                     $binaries[] = 'hg';
                     break;
             }
         }
         if ($repository->getServeOverSSH() != PhabricatorRepository::SERVE_OFF) {
             switch ($repository->getVersionControlSystem()) {
                 case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
                     $binaries[] = 'git-receive-pack';
                     $binaries[] = 'git-upload-pack';
                     break;
                 case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
                     $binaries[] = 'svnserve';
                     $binaries[] = 'svnadmin';
                     $binaries[] = 'svnlook';
                     $svnlook_check = true;
                     break;
                 case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
                     $binaries[] = 'hg';
                     break;
             }
         }
     }
     $binaries = array_unique($binaries);
     if (!$is_cluster) {
         // We're only checking for binaries if we aren't running with a cluster
         // configuration. In theory, we could check for binaries on the
         // repository host machine, but we'd need to make this more complicated
         // to do that.
         foreach ($binaries as $binary) {
             $where = Filesystem::resolveBinary($binary);
             if (!$where) {
                 $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))->setNote(pht("Unable to find this binary in the webserver's PATH. You may " . "need to configure %s.", $this->getEnvConfigLink())));
             } else {
                 $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Found Binary %s', phutil_tag('tt', array(), $binary)))->setNote(phutil_tag('tt', array(), $where)));
             }
         }
         // This gets checked generically above. However, for svn commit hooks, we
         // need this to be in environment.append-paths because subversion strips
         // PATH.
         if ($svnlook_check) {
             $where = Filesystem::resolveBinary('svnlook');
             if ($where) {
                 $path = substr($where, 0, strlen($where) - strlen('svnlook'));
                 $dirs = PhabricatorEnv::getEnvConfig('environment.append-paths');
                 $in_path = false;
                 foreach ($dirs as $dir) {
                     if (Filesystem::isDescendant($path, $dir)) {
                         $in_path = true;
                         break;
                     }
                 }
                 if (!$in_path) {
                     $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Missing Binary %s', phutil_tag('tt', array(), $binary)))->setNote(pht('Unable to find this binary in `%s`. ' . 'You need to configure %s and include %s.', 'environment.append-paths', $this->getEnvConfigLink(), $path)));
                 }
             }
         }
     }
     $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd');
     $daemon_instructions = pht('Use %s to start daemons. See %s.', phutil_tag('tt', array(), 'bin/phd start'), phutil_tag('a', array('href' => $doc_href), pht('Managing Daemons with phd')));
     $pull_daemon = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)->withDaemonClasses(array('PhabricatorRepositoryPullLocalDaemon'))->setLimit(1)->execute();
     if ($pull_daemon) {
         // TODO: In a cluster environment, we need a daemon on this repository's
         // host, specifically, and we aren't checking for that right now. This
         // is a reasonable proxy for things being more-or-less correctly set up,
         // though.
         $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Pull Daemon Running')));
     } else {
         $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Pull Daemon Not Running'))->setNote($daemon_instructions));
     }
     $task_daemon = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)->withDaemonClasses(array('PhabricatorTaskmasterDaemon'))->setLimit(1)->execute();
     if ($task_daemon) {
         $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Task Daemon Running')));
     } else {
         $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Task Daemon Not Running'))->setNote($daemon_instructions));
     }
     if ($is_cluster) {
         // Just omit this status check for now in cluster environments. We
         // could make a service call and pull it from the repository host
         // eventually.
     } else {
         if ($repository->usesLocalWorkingCopy()) {
             $local_parent = dirname($repository->getLocalPath());
             if (Filesystem::pathExists($local_parent)) {
                 $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Storage Directory OK'))->setNote(phutil_tag('tt', array(), $local_parent)));
             } else {
                 $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('No Storage Directory'))->setNote(pht('Storage directory %s does not exist, or is not readable by ' . 'the webserver. Create this directory or make it readable.', phutil_tag('tt', array(), $local_parent))));
                 return $view;
             }
             $local_path = $repository->getLocalPath();
             $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_INIT);
             if ($message) {
                 switch ($message->getStatusCode()) {
                     case PhabricatorRepositoryStatusMessage::CODE_ERROR:
                         $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Initialization Error'))->setNote($message->getParameter('message')));
                         return $view;
                     case PhabricatorRepositoryStatusMessage::CODE_OKAY:
                         if (Filesystem::pathExists($local_path)) {
                             $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Working Copy OK'))->setNote(phutil_tag('tt', array(), $local_path)));
                         } else {
                             $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Working Copy Error'))->setNote(pht('Working copy %s has been deleted, or is not ' . 'readable by the webserver. Make this directory ' . 'readable. If it has been deleted, the daemons should ' . 'restore it automatically.', phutil_tag('tt', array(), $local_path))));
                             return $view;
                         }
                         break;
                     case PhabricatorRepositoryStatusMessage::CODE_WORKING:
                         $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green')->setTarget(pht('Initializing Working Copy'))->setNote(pht('Daemons are initializing the working copy.')));
                         return $view;
                     default:
                         $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Unknown Init Status'))->setNote($message->getStatusCode()));
                         return $view;
                 }
             } else {
                 $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange')->setTarget(pht('No Working Copy Yet'))->setNote(pht('Waiting for daemons to build a working copy.')));
                 return $view;
             }
         }
     }
     $message = idx($messages, PhabricatorRepositoryStatusMessage::TYPE_FETCH);
     if ($message) {
         switch ($message->getStatusCode()) {
             case PhabricatorRepositoryStatusMessage::CODE_ERROR:
                 $message = $message->getParameter('message');
                 $suggestion = null;
                 if (preg_match('/Permission denied \\(publickey\\)./', $message)) {
                     $suggestion = pht('Public Key Error: This error usually indicates that the ' . 'keypair you have configured does not have permission to ' . 'access the repository.');
                 }
                 $message = phutil_escape_html_newlines($message);
                 if ($suggestion !== null) {
                     $message = array(phutil_tag('strong', array(), $suggestion), phutil_tag('br'), phutil_tag('br'), phutil_tag('em', array(), pht('Raw Error')), phutil_tag('br'), $message);
                 }
                 $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_WARNING, 'red')->setTarget(pht('Update Error'))->setNote($message));
                 return $view;
             case PhabricatorRepositoryStatusMessage::CODE_OKAY:
                 $ago = PhabricatorTime::getNow() - $message->getEpoch();
                 $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Updates OK'))->setNote(pht('Last updated %s (%s ago).', phabricator_datetime($message->getEpoch(), $viewer), phutil_format_relative_time_detailed($ago))));
                 break;
         }
     } else {
         $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'orange')->setTarget(pht('Waiting For Update'))->setNote(pht('Waiting for daemons to read updates.')));
     }
     if ($repository->isImporting()) {
         $progress = queryfx_all($repository->establishConnection('r'), 'SELECT importStatus, count(*) N FROM %T WHERE repositoryID = %d
       GROUP BY importStatus', id(new PhabricatorRepositoryCommit())->getTableName(), $repository->getID());
         $done = 0;
         $total = 0;
         foreach ($progress as $row) {
             $total += $row['N'] * 4;
             $status = $row['importStatus'];
             if ($status & PhabricatorRepositoryCommit::IMPORTED_MESSAGE) {
                 $done += $row['N'];
             }
             if ($status & PhabricatorRepositoryCommit::IMPORTED_CHANGE) {
                 $done += $row['N'];
             }
             if ($status & PhabricatorRepositoryCommit::IMPORTED_OWNERS) {
                 $done += $row['N'];
             }
             if ($status & PhabricatorRepositoryCommit::IMPORTED_HERALD) {
                 $done += $row['N'];
             }
         }
         if ($total) {
             $percentage = 100 * ($done / $total);
         } else {
             $percentage = 0;
         }
         // Cap this at "99.99%", because it's confusing to users when the actual
         // fraction is "99.996%" and it rounds up to "100.00%".
         if ($percentage > 99.98999999999999) {
             $percentage = 99.98999999999999;
         }
         $percentage = sprintf('%.2f%%', $percentage);
         $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_CLOCK, 'green')->setTarget(pht('Importing'))->setNote(pht('%s Complete', $percentage)));
     } else {
         $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')->setTarget(pht('Fully Imported')));
     }
     if (idx($messages, PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE)) {
         $view->addItem(id(new PHUIStatusItemView())->setIcon(PHUIStatusItemView::ICON_UP, 'indigo')->setTarget(pht('Prioritized'))->setNote(pht('This repository will be updated soon!')));
     }
     return $view;
 }
 private function renderStackTrace($trace)
 {
     $libraries = PhutilBootloader::getInstance()->getAllLibraries();
     // TODO: Make this configurable?
     $host = 'https://secure.phabricator.com';
     $browse = array('arcanist' => $host . '/diffusion/ARC/browse/origin:master/src/', 'phutil' => $host . '/diffusion/PHU/browse/origin:master/src/', 'phabricator' => $host . '/diffusion/P/browse/origin:master/src/');
     $rows = array();
     $depth = count($trace);
     foreach ($trace as $part) {
         $lib = null;
         $file = idx($part, 'file');
         $relative = $file;
         foreach ($libraries as $library) {
             $root = phutil_get_library_root($library);
             if (Filesystem::isDescendant($file, $root)) {
                 $lib = $library;
                 $relative = Filesystem::readablePath($file, $root);
                 break;
             }
         }
         $where = '';
         if (isset($part['class'])) {
             $where .= $part['class'] . '::';
         }
         if (isset($part['function'])) {
             $where .= $part['function'] . '()';
         }
         if ($file) {
             if (isset($browse[$lib])) {
                 $file_name = phutil_render_tag('a', array('href' => $browse[$lib] . $relative . '$' . $part['line'], 'title' => $file, 'target' => '_blank'), phutil_escape_html($relative));
             } else {
                 $file_name = phutil_render_tag('span', array('title' => $file), phutil_escape_html($relative));
             }
             $file_name = $file_name . ' : ' . (int) $part['line'];
         } else {
             $file_name = '<em>(Internal)</em>';
         }
         $rows[] = array($depth--, phutil_escape_html($lib), $file_name, phutil_escape_html($where));
     }
     $table = new AphrontTableView($rows);
     $table->setHeaders(array('Depth', 'Library', 'File', 'Where'));
     $table->setColumnClasses(array('n', '', '', 'wide'));
     return '<div class="exception-trace">' . '<div class="exception-trace-header">Stack Trace</div>' . $table->render() . '</div>';
 }
 public function deleteKeys(array $keys)
 {
     $this->validateKeys($keys);
     $this->lockCache(15);
     foreach ($keys as $key) {
         $path = $this->getKeyFile($key);
         Filesystem::remove($path);
         // If removing this key leaves the directory empty, clean it up. Then
         // clean up any empty parent directories.
         $path = dirname($path);
         do {
             if (!Filesystem::isDescendant($path, $this->getCacheDirectory())) {
                 break;
             }
             if (Filesystem::listDirectory($path, true)) {
                 break;
             }
             Filesystem::remove($path);
             $path = dirname($path);
         } while (true);
     }
     $this->unlockCache();
     return $this;
 }
Esempio n. 19
0
 function generateCoverageResults()
 {
     // Find all executables, not just the test executables.
     // We need to do this because the test executables may not
     // include all of the possible code (linker decides to omit
     // it from the image) so we see a skewed representation of
     // the source lines.
     $fp = popen("find {$this->repo_root} -type f -name watchman -o " . "-name \\*.a -o -name \\*.so -o -name \\*.dylib", "r");
     while (true) {
         $line = fgets($fp);
         if ($line === false) {
             break;
         }
         $obj_files[] = trim($line);
     }
     // Parse line information from the objects
     foreach ($obj_files as $object) {
         DwarfLineInfo::loadObject($object);
     }
     $CG = new CallgrindFile((string) $this->cg_file);
     $CG->parse();
     $source_files = array();
     foreach ($CG->getSourceFiles() as $filename) {
         if (Filesystem::isDescendant($filename, $this->repo_root) && !preg_match("/(thirdparty|tests)/", $filename)) {
             $source_files[$filename] = $filename;
         }
     }
     $cov = array();
     foreach ($source_files as $filename) {
         $relsrc = substr($filename, strlen($this->repo_root) + 1);
         $cov[$relsrc] = CallgrindFile::mergeSourceLineData($filename, array($CG));
     }
     $res = new ArcanistUnitTestResult();
     $res->setName('coverage');
     $res->setUserData("Collected");
     $res->setResult(ArcanistUnitTestResult::RESULT_PASS);
     $res->setCoverage($cov);
     // Stash it for review with our `arc cov` command
     $wc = ArcanistWorkingCopyIdentity::newFromPath($this->repo_root);
     $api = ArcanistRepositoryAPI::newAPIFromWorkingCopyIdentity($wc);
     $api->writeScratchFile('wman-cov.json', json_encode($cov));
     return array($res);
 }
 public function canDestroyWorkingCopy()
 {
     if ($this->isHosted()) {
         // Never destroy hosted working copies.
         return false;
     }
     $default_path = PhabricatorEnv::getEnvConfig('repository.default-local-path');
     return Filesystem::isDescendant($this->getLocalPath(), $default_path);
 }
 /**
  * Returns the EditorConfig files which affect the specified path.
  *
  * Find and parse all `.editorconfig` files between the specified path and
  * the root directory. The results are returned in the same order that they
  * should be matched.
  *
  * return list<pair<string, map>>
  */
 private function getEditorConfigs($path)
 {
     $configs = array();
     $found_root = false;
     $root = $this->root;
     do {
         $path = dirname($path);
         $file = $path . '/.editorconfig';
         if (!Filesystem::pathExists($file)) {
             continue;
         }
         $contents = Filesystem::readFile($file);
         $config = phutil_ini_decode($contents);
         if (idx($config, 'root') === true) {
             $found_root = true;
         }
         unset($config['root']);
         array_unshift($configs, array($path, $config));
         if ($found_root) {
             break;
         }
     } while ($path != $root && Filesystem::isDescendant($path, $root));
     return $configs;
 }