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)));
     }
 }
 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;
 }
 public function run()
 {
     $bootloader = PhutilBootloader::getInstance();
     $affected_modules = 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);
         if (!is_dir($path)) {
             $path = dirname($path);
         }
         if ($path == $library_root) {
             continue;
         }
         $library_path = Filesystem::readablePath($path, $library_root);
         do {
             // Add the module and all parent modules as affected modules, which
             // means we'll look for __tests__ to run here and in any containing
             // module.
             $affected_modules[$library_name . ':' . $library_path] = array('name' => $library_name, 'root' => $library_root, 'path' => $library_path);
             $library_path = dirname($library_path);
         } while ($library_path != '.');
     }
     $tests = array();
     foreach ($affected_modules as $library_info) {
         $library_name = $library_info['name'];
         $library_root = $library_info['root'];
         $module = $library_info['path'];
         if (basename($module) == '__tests__') {
             // Okay, this is a __tests__ module.
         } else {
             $exists = $bootloader->moduleExists($library_name, $module . '/__tests__');
             if ($exists) {
                 // This is a module which has a __tests__ module in it.
                 $module .= '/__tests__';
             } else {
                 // Look for a parent named __tests__.
                 $rpos = strrpos($module, '/__tests__');
                 if ($rpos === false) {
                     // No tests to run since there is no child or parent module named
                     // __tests__.
                     continue;
                 }
                 // Select the parent named __tests__.
                 $module = substr($module, 0, $rpos + strlen('/__tests__'));
             }
         }
         $module_key = $library_name . ':' . $module;
         $tests[$module_key] = array('library' => $library_name, 'root' => $library_root, 'module' => $module);
     }
     if (!$tests) {
         throw new ArcanistNoEffectException("No tests to run.");
     }
     $run_tests = array();
     foreach ($tests as $test) {
         $symbols = id(new PhutilSymbolLoader())->setType('class')->setLibrary($test['library'])->setModule($test['module'])->setAncestorClass('ArcanistPhutilTestCase')->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. You may need to rebuild the phutil library map.");
     }
     $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) {
         PhutilSymbolLoader::loadClass($test_class);
         $test_case = newv($test_class, array());
         $test_case->setEnableCoverage($enable_coverage);
         $test_case->setProjectRoot($this->getWorkingCopy()->getProjectRoot());
         $test_case->setPaths($this->getPaths());
         $results[] = $test_case->run();
     }
     if ($results) {
         $results = call_user_func_array('array_merge', $results);
     }
     return $results;
 }
 /**
  * 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;
 }
Example #5
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;
 }
 public function willLintPaths(array $paths)
 {
     if ($paths) {
         if (!xhpast_is_available()) {
             throw new Exception(xhpast_get_build_instructions());
         }
     }
     $modules = array();
     $moduleinfo = array();
     $project_root = $this->getEngine()->getWorkingCopy()->getProjectRoot();
     foreach ($paths as $path) {
         $absolute_path = $project_root . '/' . $path;
         $library_root = phutil_get_library_root_for_path($absolute_path);
         if (!$library_root) {
             continue;
         }
         if ($this->isPhutilLibraryMetadata($path)) {
             continue;
         }
         $library_name = phutil_get_library_name_for_root($library_root);
         if (!is_dir($path)) {
             $path = dirname($path);
         }
         $path = Filesystem::resolvePath($path, $project_root);
         if ($path == $library_root) {
             continue;
         }
         $module_name = Filesystem::readablePath($path, $library_root);
         $module_key = $library_name . ':' . $module_name;
         if (empty($modules[$module_key])) {
             $modules[$module_key] = $module_key;
             $this->setModuleInfo($module_key, array('library' => $library_name, 'root' => $library_root, 'module' => $module_name));
         }
     }
     if (!$modules) {
         return;
     }
     $modules = array_keys($modules);
     $arc_root = phutil_get_library_root('arcanist');
     $bin = dirname($arc_root) . '/scripts/phutil_analyzer.php';
     $futures = array();
     foreach ($modules as $mkey => $key) {
         $disk_path = $this->getModulePathOnDisk($key);
         if (Filesystem::pathExists($disk_path)) {
             $futures[$key] = new ExecFuture('%s %s', $bin, $disk_path);
         } else {
             // This can occur in git when you add a module in HEAD and then remove
             // it in unstaged changes in the working copy. Just ignore it.
             unset($modules[$mkey]);
         }
     }
     $requirements = array();
     foreach (Futures($futures)->limit(16) as $key => $future) {
         $requirements[$key] = $future->resolveJSON();
     }
     $dependencies = array();
     $futures = array();
     foreach ($requirements as $key => $requirement) {
         foreach ($requirement['messages'] as $message) {
             list($where, $text, $code, $description) = $message;
             if ($where) {
                 $where = array($where);
             }
             $this->raiseLintInModule($key, $code, $description, $where, $text);
         }
         foreach ($requirement['requires']['module'] as $req_module => $where) {
             if (isset($requirements[$req_module])) {
                 $dependencies[$req_module] = $requirements[$req_module];
             } else {
                 list($library_name, $module_name) = explode(':', $req_module);
                 $library_root = phutil_get_library_root($library_name);
                 $this->setModuleInfo($req_module, array('library' => $library_name, 'root' => $library_root, 'module' => $module_name));
                 $disk_path = $this->getModulePathOnDisk($req_module);
                 if (Filesystem::pathExists($disk_path)) {
                     $futures[$req_module] = new ExecFuture('%s %s', $bin, $disk_path);
                 } else {
                     $dependencies[$req_module] = array();
                 }
             }
         }
     }
     foreach (Futures($futures)->limit(16) as $key => $future) {
         $dependencies[$key] = $future->resolveJSON();
     }
     foreach ($requirements as $key => $spec) {
         $deps = array_intersect_key($dependencies, $spec['requires']['module']);
         $this->lintModule($key, $spec, $deps);
     }
 }
 /**
  * Get the root directory for the library currently being tested.
  */
 protected function getLibraryRoot()
 {
     $caller = id(new ReflectionClass($this))->getFileName();
     return phutil_get_library_root_for_path($caller);
 }
function phutil_get_current_library_name()
{
    $caller = head(debug_backtrace(false));
    $root = phutil_get_library_root_for_path($caller['file']);
    return phutil_get_library_name_for_root($root);
}