private function runTests() { $root = $this->getWorkingCopy()->getProjectRoot(); $script = $this->getConfiguredScript(); $path = $this->getConfiguredTestResultPath(); foreach (glob($root . DIRECTORY_SEPARATOR . $path . "/*.xml") as $filename) { // Remove existing files so we cannot report old results $this->unlink($filename); } // Provide changed paths to process putenv("ARCANIST_DIFF_PATHS=" . implode(PATH_SEPARATOR, $this->getPaths())); $future = new ExecFuture('%C %s', $script, $path); $future->setCWD($root); $err = null; try { $future->resolvex(); } catch (CommandException $exc) { $err = $exc; } $results = $this->parseTestResults($root . DIRECTORY_SEPARATOR . $path); if ($err) { $result = new ArcanistUnitTestResult(); $result->setName('Unit Test Script'); $result->setResult(ArcanistUnitTestResult::RESULT_BROKEN); $result->setUserData("ERROR: Command failed with code {$err->getError()}\nCOMMAND: `{$err->getCommand()}`"); $results[] = $result; } return $results; }
/** * Returns an array of the full canonical paths to all the Maven directories * (directories containing pom.xml files) in the project. */ private function findMavenDirectories() { if (file_exists($this->project_root . "/.git")) { // The fastest way to find all the pom.xml files is to let git scan // its index. $future = new ExecFuture('git ls-files \\*/pom.xml'); } else { // Not a git repo. Do it the old-fashioned way. $future = new ExecFuture('find . -name pom.xml -print'); } // TODO: This will find *all* the pom.xml files in the working copy. // Need to obey the optional paths argument to "arc unit" to let users // run just a subset of tests. $future->setCWD($this->project_root); list($stdout) = $future->resolvex(); $poms = explode("\n", trim($stdout)); if (!$poms) { throw new Exception("No pom.xml files found"); } $maven_dirs = array_map(function ($pom) { $maven_dir = dirname($pom); return realpath($this->project_root . '/' . $maven_dir); }, $poms); return $maven_dirs; }
public function buildTestFuture($junit_tmp, $cover_tmp) { $paths = $this->getPaths(); $config_manager = $this->getConfigurationManager(); $coverage_command = $config_manager->getConfigFromAnySource('unit.golang.command'); $cmd_line = csprintf($coverage_command, $junit_tmp, $cover_tmp); $future = new ExecFuture('%C', $cmd_line); $future->setCWD($this->projectRoot); return $future; }
public function run() { $working_copy = $this->getWorkingCopy(); $this->projectRoot = $working_copy->getProjectRoot(); $junit_tmp = new TempFile(); $cover_tmp = new TempFile(); $future = $this->buildTestFuture($junit_tmp, $cover_tmp); $future->resolvex(); $future = new ExecFuture('coverage xml -o %s', $cover_tmp); $future->setCWD($this->projectRoot); $future->resolvex(); return $this->parseTestResults($junit_tmp, $cover_tmp); }
public function __construct($sockname, $repo_root, $args, array $command) { $this->command = json_encode($command); $console = PhutilConsole::getConsole(); $console->writeLog("cli query: %s\n", $this->command); $future = new ExecFuture("%C {$args} -U %s %s %s %s %s", "{$repo_root}/watchman", $sockname, '--no-pretty', '--no-spawn', '--no-local', '-j'); $cwd = getcwd(); if (!$cwd) { throw new Exception("can't figure out my cwd!?"); } $future->setCWD($cwd); $future->write($this->command . "\n"); parent::__construct($future); }
public function willLintPaths(array $paths) { $script = $this->getConfiguredScript(); $root = $this->getEngine()->getWorkingCopy()->getProjectRoot(); $futures = array(); foreach ($paths as $path) { $future = new ExecFuture('%C %s', $script, $path); $future->setCWD($root); $futures[$path] = $future; } foreach (Futures($futures)->limit(4) as $path => $future) { list($stdout) = $future->resolvex(); $this->output[$path] = $stdout; } }
private function checkRequirements() { $working_copy = $this->getWorkingCopy(); $project_root = $working_copy->getProjectRoot(); $piplint_files = $this->getConfigurationManager()->getConfigFromAnySource('unit.piplint.files'); if (empty($piplint_files)) { return; } $args = array('piplint'); $args = array_merge($args, $piplint_files); $cmd = implode(' ', $args); $future = new ExecFuture($cmd); $future->setCWD($project_root); $future->resolvex(); }
public function run() { $working_copy = $this->getWorkingCopy(); $this->projectRoot = $working_copy->getProjectRoot(); $junit_tmp = new TempFile(); $cover_tmp = new TempFile(); $future = $this->buildTestFuture($junit_tmp, $cover_tmp); list($err, $stdout, $stderr) = $future->resolve(); if (!Filesystem::pathExists($junit_tmp)) { throw new CommandException(pht('Command failed with error #%s!', $err), $future->getCommand(), $err, $stdout, $stderr); } $future = new ExecFuture('coverage xml -o %s', $cover_tmp); $future->setCWD($this->projectRoot); $future->resolvex(); return $this->parseTestResults($junit_tmp, $cover_tmp); }
public function run() { $working_copy = $this->getWorkingCopy(); $this->projectRoot = $working_copy->getProjectRoot(); $future = new ExecFuture('npm run coverage'); $future->setCWD($this->projectRoot); list($err, $stdout, $stderr) = $future->resolve(); $result = new ArcanistUnitTestResult(); $result->setName("Node test engine"); $result->setUserData($stdout); if ($err) { $result->setResult(ArcanistUnitTestResult::RESULT_FAIL); } else { $result->setResult(ArcanistUnitTestResult::RESULT_PASS); } return array($result); }
private function runTests() { $root = $this->getWorkingCopy()->getProjectRoot(); $script = $this->getConfiguredScript(); $path = $this->getConfiguredTestResultPath(); foreach (glob($path . "/*.xml") as $filename) { // Remove existing files so we cannot report old results $this->unlink($filename); } $future = new ExecFuture('%C %s', $script, $path); $future->setCWD($root); try { $future->resolvex(); } catch (CommandException $exc) { if ($exc->getError() != 0) { throw $exc; } } return $this->parseTestResults($path); }
/** * Build the future for running a unit test. This can be overridden to enable * support for code coverage via another tool. * * @param string Name of the test assembly. * @return array The future, output filename and coverage filename * stored in an array. */ protected function buildTestFuture($test_assembly) { // FIXME: Can't use TempFile here as xUnit doesn't like // UNIX-style full paths. It sees the leading / as the // start of an option flag, even when quoted. $xunit_temp = Filesystem::readRandomCharacters(10) . '.results.xml'; if (file_exists($xunit_temp)) { unlink($xunit_temp); } $future = new ExecFuture('%C %s /xml %s', trim($this->runtimeEngine . ' ' . $this->testEngine), $test_assembly, $xunit_temp); $folder = Filesystem::resolvePath($this->projectRoot); $future->setCWD($folder); $combined = $folder . '/' . $xunit_temp; if (phutil_is_windows()) { $combined = $folder . '\\' . $xunit_temp; } return array($future, $combined, null); }
protected final function launchDaemon($class, array $argv, $debug) { $daemon = $this->findDaemonClass($class); $console = PhutilConsole::getConsole(); if ($debug) { if ($argv) { $console->writeOut(pht("Launching daemon \"%s\" in debug mode (not daemonized) " . "with arguments %s.\n", $daemon, csprintf('%LR', $argv))); } else { $console->writeOut(pht("Launching daemon \"%s\" in debug mode (not daemonized).\n", $daemon)); } } else { if ($argv) { $console->writeOut(pht("Launching daemon \"%s\" with arguments %s.\n", $daemon, csprintf('%LR', $argv))); } else { $console->writeOut(pht("Launching daemon \"%s\".\n", $daemon)); } } foreach ($argv as $key => $arg) { $argv[$key] = escapeshellarg($arg); } $flags = array(); if ($debug || PhabricatorEnv::getEnvConfig('phd.trace')) { $flags[] = '--trace'; } if ($debug || PhabricatorEnv::getEnvConfig('phd.verbose')) { $flags[] = '--verbose'; } if (!$debug) { $flags[] = '--daemonize'; } if (!$debug) { $log_file = $this->getLogDirectory() . '/daemons.log'; $flags[] = csprintf('--log=%s', $log_file); } $pid_dir = $this->getPIDDirectory(); // TODO: This should be a much better user experience. Filesystem::assertExists($pid_dir); Filesystem::assertIsDirectory($pid_dir); Filesystem::assertWritable($pid_dir); $flags[] = csprintf('--phd=%s', $pid_dir); $command = csprintf('./phd-daemon %s %C %C', $daemon, implode(' ', $flags), implode(' ', $argv)); $phabricator_root = dirname(phutil_get_library_root('phabricator')); $daemon_script_dir = $phabricator_root . '/scripts/daemon/'; if ($debug) { // Don't terminate when the user sends ^C; it will be sent to the // subprocess which will terminate normally. pcntl_signal(SIGINT, array(__CLASS__, 'ignoreSignal')); echo "\n phabricator/scripts/daemon/ \$ {$command}\n\n"; phutil_passthru('(cd %s && exec %C)', $daemon_script_dir, $command); } else { $future = new ExecFuture('exec %C', $command); // Play games to keep 'ps' looking reasonable. $future->setCWD($daemon_script_dir); $future->resolvex(); } }
private function buildAtomizerFutures(array $file_atomizers) { $atomizers = array(); foreach ($file_atomizers as $file => $atomizer) { $atomizers[$atomizer][] = $file; } $root = dirname(phutil_get_library_root('phabricator')); $config_root = $this->getConfig('root'); $bar = id(new PhutilConsoleProgressBar())->setTotal(count($file_atomizers)); $futures = array(); foreach ($atomizers as $class => $files) { foreach (array_chunk($files, 32) as $chunk) { $future = new ExecFuture('%s atomize --ugly --book %s --atomizer %s -- %Ls', $root . '/bin/diviner', $this->getBookConfigPath(), $class, $chunk); $future->setCWD($config_root); $futures[] = $future; $bar->update(count($chunk)); } } $bar->done(); return $futures; }
protected final function buildFutures(array $paths) { $executable = $this->getExecutableCommand(); $bin = csprintf('%C %Ls', $executable, $this->getCommandFlags()); $futures = array(); foreach ($paths as $path) { $disk_path = $this->getEngine()->getFilePathOnDisk($path); $path_argument = $this->getPathArgumentForLinterFuture($disk_path); $future = new ExecFuture('%C %C', $bin, $path_argument); $future->setCWD($this->getProjectRoot()); $futures[$path] = $future; } return $futures; }
/** * Handle resolving "arc:*" rules. */ private function resolveArcRule($rule, $name, $source) { switch ($name) { case 'verbose': $this->verbose = true; $this->log("Enabled verbose mode."); break; case 'prompt': $reason = "it is what you typed when prompted."; $this->api->setBaseCommitExplanation($reason); return phutil_console_prompt('Against which commit?'); case 'local': case 'global': case 'project': case 'args': case 'system': // Push the other source on top of the list. array_unshift($this->try, $name); $this->log("Switching to source '{$name}'."); return false; case 'yield': // Cycle this source to the end of the list. $this->try[] = array_shift($this->try); $this->log("Yielding processing of rules from '{$source}'."); return false; case 'halt': // Dump the whole stack. $this->try = array(); $this->log("Halting all rule processing."); return false; case 'skip': return null; case 'empty': case 'upstream': case 'outgoing': case 'bookmark': case 'amended': case 'this': return $this->api->resolveBaseCommitRule($rule, $source); default: $matches = null; if (preg_match('/^exec\\((.*)\\)$/', $name, $matches)) { $root = $this->api->getWorkingCopyIdentity()->getProjectRoot(); $future = new ExecFuture('%C', $matches[1]); $future->setCWD($root); list($err, $stdout) = $future->resolve(); if (!$err) { return trim($stdout); } else { return null; } } else { if (preg_match('/^nodiff\\((.*)\\)$/', $name, $matches)) { return $this->api->resolveBaseCommitRule($rule, $source); } } throw new ArcanistUsageException("Base commit rule '{$rule}' (from source '{$source}') " . "is not a recognized rule."); } }
private function executeDaemonLaunchCommand($command, $daemon_script_dir, array $config, $run_as_user = null) { $is_sudo = false; if ($run_as_user) { // If anything else besides sudo should be // supported then insert it here (runuser, su, ...) $command = csprintf('sudo -En -u %s -- %C', $run_as_user, $command); $is_sudo = true; } $future = new ExecFuture('exec %C', $command); // Play games to keep 'ps' looking reasonable. $future->setCWD($daemon_script_dir); $future->write(json_encode($config)); list($stdout, $stderr) = $future->resolvex(); if ($is_sudo) { // On OSX, `sudo -n` exits 0 when the user does not have permission to // switch accounts without a password. This is not consistent with // sudo on Linux, and seems buggy/broken. Check for this by string // matching the output. if (preg_match('/sudo: a password is required/', $stderr)) { throw new Exception(pht('sudo exited with a zero exit code, but emitted output ' . 'consistent with failure under OSX.')); } } }
private function parseTestResultFile($target) { $path = "bazel-testlogs/" . str_replace(":", "/", substr($target, 2)) . "/test.cache_status"; $future = new ExecFuture("bazel-bin/kythe/go/util/tools/print_test_status %s", $path); $future->setCWD($this->project_root); return json_decode($future->resolvex()[0]); }
public function launchDaemon($daemon, array $argv, $debug = false) { $symbols = $this->loadAvailableDaemonClasses(); $symbols = ipull($symbols, 'name', 'name'); if (empty($symbols[$daemon])) { throw new Exception("Daemon '{$daemon}' is not known."); } $pid_dir = $this->getControlDirectory('pid'); $log_dir = $this->getControlDirectory('log') . '/daemons.log'; $libphutil_root = dirname(phutil_get_library_root('phutil')); $launch_daemon = $libphutil_root . '/scripts/daemon/'; // TODO: This should be a much better user experience. Filesystem::assertExists($pid_dir); Filesystem::assertIsDirectory($pid_dir); Filesystem::assertWritable($pid_dir); foreach ($argv as $key => $arg) { $argv[$key] = escapeshellarg($arg); } $bootloader = PhutilBootloader::getInstance(); $all_libraries = $bootloader->getAllLibraries(); $non_default_libraries = array_diff($all_libraries, array('phutil', 'phabricator')); $extra_libraries = array(); foreach ($non_default_libraries as $library) { $extra_libraries[] = csprintf('--load-phutil-library=%s', phutil_get_library_root($library)); } $command = csprintf("./launch_daemon.php " . "%s " . "--load-phutil-library=%s " . "%C " . "--conduit-uri=%s " . "--phd=%s " . ($debug ? '--trace ' : '--daemonize '), $daemon, phutil_get_library_root('phabricator'), implode(' ', $extra_libraries), PhabricatorEnv::getURI('/api/'), $pid_dir); if (!$debug) { // If we're running "phd debug", send output straight to the console // instead of to a logfile. $command = csprintf("%C --log=%s", $command, $log_dir); } // Append the daemon's argv. $command = csprintf("%C %C", $command, implode(' ', $argv)); if ($debug) { // Don't terminate when the user sends ^C; it will be sent to the // subprocess which will terminate normally. pcntl_signal(SIGINT, array('PhabricatorDaemonControl', 'ignoreSignal')); echo "\n libphutil/scripts/daemon/ \$ {$command}\n\n"; phutil_passthru('(cd %s && exec %C)', $launch_daemon, $command); } else { $future = new ExecFuture('exec %C', $command); // Play games to keep 'ps' looking reasonable. $future->setCWD($launch_daemon); $future->resolvex(); } }
private static function discoverGitBaseDirectory($root) { try { // NOTE: This awkward construction is to make sure things work on Windows. $future = new ExecFuture('git rev-parse --show-cdup'); $future->setCWD($root); list($stdout) = $future->resolvex(); return Filesystem::resolvePath(rtrim($stdout, "\n"), $root); } catch (CommandException $ex) { if (preg_match('/^fatal: Not a git repository/', $ex->getStdErr())) { return null; } throw $ex; } }
/** * Overridden version of `buildTestFuture` so that the unit test can be run * via `cscover`, which instruments assemblies and reports on code coverage. * * @param string Name of the test assembly. * @return array The future, output filename and coverage filename * stored in an array. */ protected function buildTestFuture($test_assembly) { if ($this->getEnableCoverage() === false) { return parent::buildTestFuture($test_assembly); } // FIXME: Can't use TempFile here as xUnit doesn't like // UNIX-style full paths. It sees the leading / as the // start of an option flag, even when quoted. $xunit_temp = Filesystem::readRandomCharacters(10) . '.results.xml'; if (file_exists($xunit_temp)) { unlink($xunit_temp); } $cover_temp = new TempFile(); $cover_temp->setPreserveFile(true); $xunit_cmd = $this->runtimeEngine; $xunit_args = null; if ($xunit_cmd === '') { $xunit_cmd = $this->testEngine; $xunit_args = csprintf('%s /xml %s', $test_assembly, $xunit_temp); } else { $xunit_args = csprintf('%s %s /xml %s', $this->testEngine, $test_assembly, $xunit_temp); } $assembly_dir = dirname($test_assembly); $assemblies_to_instrument = array(); foreach (Filesystem::listDirectory($assembly_dir) as $file) { if (substr($file, -4) == '.dll' || substr($file, -4) == '.exe') { if ($this->assemblyShouldBeInstrumented($file)) { $assemblies_to_instrument[] = $assembly_dir . DIRECTORY_SEPARATOR . $file; } } } if (count($assemblies_to_instrument) === 0) { return parent::buildTestFuture($test_assembly); } $future = new ExecFuture('%C -o %s -c %s -a %s -w %s %Ls', trim($this->runtimeEngine . ' ' . $this->coverEngine), $cover_temp, $xunit_cmd, $xunit_args, $assembly_dir, $assemblies_to_instrument); $future->setCWD(Filesystem::resolvePath($this->projectRoot)); return array($future, $assembly_dir . DIRECTORY_SEPARATOR . $xunit_temp, $cover_temp); }
/** * Run the script on each file to be linted. * * @task lint */ public function willLintPaths(array $paths) { $root = $this->getProjectRoot(); $futures = array(); foreach ($paths as $path) { $future = new ExecFuture('%C %s', $this->script, $path); $future->setCWD($root); $futures[$path] = $future; } $futures = id(new FutureIterator($futures))->limit(4); foreach ($futures as $path => $future) { list($stdout) = $future->resolvex(); $this->output[$path] = $stdout; } }
public function run() { if ($this->shouldRunSilently()) { echo "Running daemon '{$this->daemon}' silently. Use '--trace' or " . "'--verbose' to produce debugging output.\n"; } $root = phutil_get_library_root('phutil'); $root = dirname($root); $exec_dir = $root . '/scripts/daemon/exec/'; // NOTE: PHP implements proc_open() by running 'sh -c'. On most systems this // is bash, but on Ubuntu it's dash. When you proc_open() using bash, you // get one new process (the command you ran). When you proc_open() using // dash, you get two new processes: the command you ran and a parent // "dash -c" (or "sh -c") process. This means that the child process's PID // is actually the 'dash' PID, not the command's PID. To avoid this, use // 'exec' to replace the shell process with the real process; without this, // the child will call posix_getppid(), be given the pid of the 'sh -c' // process, and send it SIGUSR1 to keepalive which will terminate it // immediately. We also won't be able to do process group management because // the shell process won't properly posix_setsid() so the pgid of the child // won't be meaningful. // Format the exec command, which looks something like: // // exec ./exec_daemon DaemonName --trace -- --no-discovery $argv = array(); $argv[] = csprintf('exec ./exec_daemon.php %s', $this->daemon); foreach ($this->argv as $k => $arg) { $argv[] = csprintf('%s', $arg); } $argv[] = '--'; foreach ($this->moreArgs as $k => $arg) { $argv[] = csprintf('%s', $arg); } $command = implode(' ', $argv); while (true) { $this->logMessage('INIT', 'Starting process.'); $future = new ExecFuture('%C', $command); $future->setCWD($exec_dir); $future->setStdoutSizeLimit($this->captureBufferSize); $future->setStderrSizeLimit($this->captureBufferSize); $this->deadline = time() + $this->deadlineTimeout; $this->heartbeat = time() + self::HEARTBEAT_WAIT; $future->isReady(); $this->childPID = $future->getPID(); do { do { if ($this->traceMemory) { $memuse = number_format(memory_get_usage() / 1024, 1); $this->logMessage('RAMS', 'Overseer Memory Usage: ' . $memuse . ' KB'); } // We need a shortish timeout here so we can run the tick handler // frequently in order to process signals. $result = $future->resolve(1); list($stdout, $stderr) = $future->read(); $stdout = trim($stdout); $stderr = trim($stderr); if (strlen($stdout)) { $this->logMessage('STDO', $stdout); } if (strlen($stderr)) { $this->logMessage('STDE', $stderr); } $future->discardBuffers(); if ($result !== null) { list($err) = $result; if ($err) { $this->logMessage('FAIL', 'Process exited with error ' . $err . '.', $err); } else { $this->logMessage('DONE', 'Process exited successfully.'); } break 2; } if ($this->heartbeat < time()) { $this->heartbeat = time() + self::HEARTBEAT_WAIT; $this->dispatchEvent(self::EVENT_DID_HEARTBEAT); } } while (time() < $this->deadline); $this->logMessage('HANG', 'Hang detected. Restarting process.'); $this->annihilateProcessGroup(); } while (false); if ($this->inGracefulShutdown) { // If we just exited because of a graceful shutdown, break now. break; } $this->logMessage('WAIT', 'Waiting to restart process.'); sleep(self::RESTART_WAIT); if ($this->inGracefulShutdown) { // If we were awakend by a graceful shutdown, break now. break; } } // This is a clean exit after a graceful shutdown. $this->dispatchEvent(self::EVENT_WILL_EXIT); exit(0); }
public function launchDaemon($daemon, array $argv, $debug = false) { $symbols = $this->loadAvailableDaemonClasses(); $symbols = ipull($symbols, 'name', 'name'); if (empty($symbols[$daemon])) { throw new Exception("Daemon '{$daemon}' is not loaded, misspelled or abstract."); } $libphutil_root = dirname(phutil_get_library_root('phutil')); $launch_daemon = $libphutil_root . '/scripts/daemon/'; foreach ($argv as $key => $arg) { $argv[$key] = escapeshellarg($arg); } $flags = array(); if ($debug || PhabricatorEnv::getEnvConfig('phd.trace')) { $flags[] = '--trace'; } if ($debug || PhabricatorEnv::getEnvConfig('phd.verbose')) { $flags[] = '--verbose'; } if (!$debug) { $flags[] = '--daemonize'; } $bootloader = PhutilBootloader::getInstance(); foreach ($bootloader->getAllLibraries() as $library) { if ($library == 'phutil') { // No need to load libphutil, it's necessarily loaded implicitly by the // daemon itself. continue; } $flags[] = csprintf('--load-phutil-library=%s', phutil_get_library_root($library)); } $flags[] = csprintf('--conduit-uri=%s', PhabricatorEnv::getURI('/api/')); if (!$debug) { $log_file = $this->getLogDirectory() . '/daemons.log'; $flags[] = csprintf('--log=%s', $log_file); } $pid_dir = $this->getPIDDirectory(); // TODO: This should be a much better user experience. Filesystem::assertExists($pid_dir); Filesystem::assertIsDirectory($pid_dir); Filesystem::assertWritable($pid_dir); $flags[] = csprintf('--phd=%s', $pid_dir); $command = csprintf('./launch_daemon.php %s %C %C', $daemon, implode(' ', $flags), implode(' ', $argv)); if ($debug) { // Don't terminate when the user sends ^C; it will be sent to the // subprocess which will terminate normally. pcntl_signal(SIGINT, array('PhabricatorDaemonControl', 'ignoreSignal')); echo "\n libphutil/scripts/daemon/ \$ {$command}\n\n"; phutil_passthru('(cd %s && exec %C)', $launch_daemon, $command); } else { $future = new ExecFuture('exec %C', $command); // Play games to keep 'ps' looking reasonable. $future->setCWD($launch_daemon); $future->resolvex(); } }
private static function discoverGitBaseDirectory($root) { try { // NOTE: This awkward construction is to make sure things work on Windows. $future = new ExecFuture('git rev-parse --show-cdup'); $future->setCWD($root); list($stdout) = $future->resolvex(); return Filesystem::resolvePath(rtrim($stdout, "\n"), $root); } catch (CommandException $ex) { // This might be because the $root isn't a Git working copy, or the user // might not have Git installed at all so the `git` command fails. Assume // that users trying to work with git working copies will have a working // `git` binary. return null; } }
private function processCoverageResults($project_root, $results) { $time_start = microtime_float(); // generate annotated source files to find out which lines have // coverage // limit files to only those "*.py" files in getPaths() $pythonPathsStr = join(" ", $this->getPythonPaths()); $future = new ExecFuture("coverage annotate {$pythonPathsStr}"); $future->setCWD($project_root); try { $future->resolvex(); } catch (CommandException $exc) { if ($exc->getError() > 1) { // 'nose' returns 1 when tests are failing/broken. throw $exc; } } // store all the coverage results for this project $coverageArray = array(); $lines_total = 0; $lines_executable = 0; $lines_not_executable = 0; $lines_covered = 0; $lines_not_covered = 0; // walk through project directory, searching for all ",cover" files // that coverage.py left behind foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator(".")) as $path) { // paths are given as "./path/to/file.py,cover", so match the // "path/to/file.py" part if (!preg_match(":^\\./(.*),cover\$:", $path, $matches)) { continue; } $srcFilePath = $matches[1]; $coverageStr = ""; foreach (file($path) as $coverLine) { /* python coverage > executed ! missing(not executed) - excluded phab coverage N Not executable. This is a comment or whitespace which should be ignored when computing test coverage. C Covered. This line has test coverage. U Uncovered. This line is executable but has no test coverage. X Unreachable. If your coverage analysis can detect unreachable code, you can report it here. */ $lines_total++; switch ($coverLine[0]) { case '>': $lines_covered++; $lines_executable++; $coverageStr .= 'C'; break; case '!': $lines_not_covered++; $lines_executable++; $coverageStr .= 'U'; break; case ' ': $lines_not_executable++; $coverageStr .= 'N'; break; case '-': $coverageStr .= 'X'; break; default: break; } } // delete the ,cover file unlink($path); // only add to coverage report if the path was originally // specified by arc if (in_array($srcFilePath, $this->getPaths())) { $coverageArray[$srcFilePath] = $coverageStr; } } $lines_percentage = bcdiv($lines_covered, $lines_executable, 4) * 100; $time_end = microtime_float(); $time = $time_end - $time_start; // python does not support per-test coverage results so just put all the coverage // in a single 'coverage test' $result = new ArcanistUnitTestResult(); $result->setNamespace('coverage'); $result->setName('coverage'); $result->setResult('pass'); $result->setDuration($time); $result->setUserData("coverage: {$lines_percentage}% executable: {$lines_executable} / covered: {$lines_covered}"); $result->setCoverage($coverageArray); $results[] = $result; return $results; }
protected function executeRepositoryOperations() { $repository = $this->getRepository(); $args = $this->getArgs(); if (!$args->getArg('tunnel')) { throw new Exception(pht('Expected `%s`!', 'svnserve -t')); } if ($this->shouldProxy()) { $command = $this->getProxyCommand(); $this->isProxying = true; $cwd = null; } else { $command = csprintf('svnserve -t --tunnel-user=%s', $this->getUser()->getUsername()); $cwd = PhabricatorEnv::getEmptyCWD(); } $command = PhabricatorDaemon::sudoCommandAsDaemonUser($command); $future = new ExecFuture('%C', $command); // If we're receiving a commit, svnserve will fail to execute the commit // hook with an unhelpful error if the CWD isn't readable by the user we // are sudoing to. Switch to a readable, empty CWD before running // svnserve. See T10941. if ($cwd !== null) { $future->setCWD($cwd); } $this->inProtocol = new DiffusionSubversionWireProtocol(); $this->outProtocol = new DiffusionSubversionWireProtocol(); $this->command = id($this->newPassthruCommand())->setIOChannel($this->getIOChannel())->setCommandChannelFromExecFuture($future)->setWillWriteCallback(array($this, 'willWriteMessageCallback'))->setWillReadCallback(array($this, 'willReadMessageCallback')); $this->command->setPauseIOReads(true); $err = $this->command->execute(); if (!$err && $this->didSeeWrite) { $this->getRepository()->writeStatusMessage(PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE, PhabricatorRepositoryStatusMessage::CODE_OKAY); } return $err; }
/** * Starts a Mercurial process which can actually handle requests. * * @return ArcanistHgServerChannel Channel to the Mercurial server. * @task hg */ private function startMercurialProcess() { // NOTE: "cmdserver.log=-" makes Mercurial use the 'd'ebug channel for // log messages. $future = new ExecFuture('HGPLAIN=1 hg --config cmdserver.log=- serve --cmdserver pipe'); $future->setCWD($this->workingCopy); $channel = new PhutilExecChannel($future); $hg = new ArcanistHgServerChannel($channel); // The server sends a "hello" message with capability and encoding // information. Save it and forward it to clients when they connect. $this->hello = $hg->waitForMessage(); return $hg; }
public function run() { if ($this->shouldRunSilently()) { echo "Running daemon '{$this->daemon}' silently. Use '--trace' or " . "'--verbose' to produce debugging output.\n"; } $root = phutil_get_library_root('phutil'); $root = dirname($root); $exec_dir = $root . '/scripts/daemon/exec/'; // NOTE: PHP implements proc_open() by running 'sh -c'. On most systems this // is bash, but on Ubuntu it's dash. When you proc_open() using bash, you // get one new process (the command you ran). When you proc_open() using // dash, you get two new processes: the command you ran and a parent // "dash -c" (or "sh -c") process. This means that the child process's PID // is actually the 'dash' PID, not the command's PID. To avoid this, use // 'exec' to replace the shell process with the real process; without this, // the child will call posix_getppid(), be given the pid of the 'sh -c' // process, and send it SIGUSR1 to keepalive which will terminate it // immediately. We also won't be able to do process group management because // the shell process won't properly posix_setsid() so the pgid of the child // won't be meaningful. $exec_daemon = './exec_daemon.php'; $argv = $this->argv; array_unshift($argv, 'exec', $exec_daemon, $this->daemon); foreach ($argv as $k => $arg) { $argv[$k] = escapeshellarg($arg); } $command = implode(' ', $argv); while (true) { $this->logMessage('INIT', 'Starting process.'); $future = new ExecFuture($command); $future->setCWD($exec_dir); $future->setStdoutSizeLimit($this->captureBufferSize); $future->setStderrSizeLimit($this->captureBufferSize); $this->deadline = time() + $this->deadlineTimeout; $this->heartbeat = time() + self::HEARTBEAT_WAIT; $future->isReady(); $this->childPID = $future->getPID(); do { do { if ($this->traceMemory) { $memuse = number_format(memory_get_usage() / 1024, 1); $this->logMessage('RAMS', 'Overseer Memory Usage: ' . $memuse . ' KB'); } // We need a shortish timeout here so we can run the tick handler // frequently in order to process signals. $result = $future->resolve(1); list($stdout, $stderr) = $future->read(); $stdout = trim($stdout); $stderr = trim($stderr); if (strlen($stdout)) { $this->logMessage('STDO', $stdout, $stdout); } if (strlen($stderr)) { $this->logMessage('STDE', $stderr, $stderr); } $future->discardBuffers(); if ($result !== null) { list($err) = $result; if ($err) { $this->logMessage('FAIL', 'Process exited with error ' . $err . '.', $err); } else { $this->logMessage('DONE', 'Process exited successfully.'); } break 2; } if ($this->heartbeat < time()) { $this->heartbeat = time() + self::HEARTBEAT_WAIT; if ($this->conduitURI) { try { $this->conduit = new ConduitClient($this->conduitURI); $this->conduit->callMethodSynchronous('daemon.setstatus', array('daemonLogID' => $this->daemonLogID, 'status' => 'run')); } catch (Exception $ex) { } } } } while (time() < $this->deadline); $this->logMessage('HANG', 'Hang detected. Restarting process.'); $this->annihilateProcessGroup(); } while (false); $this->logMessage('WAIT', 'Waiting to restart process.'); sleep($this->restartDelay); } }
protected final function buildFutures(array $paths) { $this->getAnalyzeCommands(); $futures = array(); foreach ($paths as $path) { $disk_path = $this->getEngine()->getFilePathOnDisk($path); if (!array_key_exists($disk_path, $this->analyzeCommands)) { throw new Exception(sprintf("%s\n\nNo command for path %s. Is it included in the project?\n", pht('Linter failed to parse xcodebuild commands.'), $path)); } $future = new ExecFuture('%C', $this->analyzeCommands[$disk_path]); $future->setCWD($this->getProjectRoot()); $futures[$path] = $future; } return $futures; }
protected final function buildFutures(array $paths) { $executable = $this->getExecutableCommand(); $bin = csprintf('%C %Ls', $executable, $this->getCommandFlags()); $futures = array(); foreach ($paths as $path) { if ($this->supportsReadDataFromStdin()) { $future = new ExecFuture('%C %C', $bin, $this->getReadDataFromStdinFilename()); $future->write($this->getEngine()->loadData($path)); } else { // TODO: In commit hook mode, we need to do more handling here. $disk_path = $this->getEngine()->getFilePathOnDisk($path); $path_argument = $this->getPathArgumentForLinterFuture($disk_path); $future = new ExecFuture('%C %C', $bin, $path_argument); } $future->setCWD($this->getEngine()->getWorkingCopy()->getProjectRoot()); $futures[$path] = $future; } return $futures; }