diff --git a/src/Composer/Downloader/PerforceDownloader.php b/src/Composer/Downloader/PerforceDownloader.php new file mode 100644 index 000000000..6ddea153f --- /dev/null +++ b/src/Composer/Downloader/PerforceDownloader.php @@ -0,0 +1,96 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + + +namespace Composer\Downloader; + +use Composer\Package\PackageInterface; +use Composer\Repository\VcsRepository; +use Composer\Util\Perforce; + +/** + * @author Matt Whittom + */ +class PerforceDownloader extends VcsDownloader +{ + protected $perforce; + protected $perforceInjected = false; + + /** + * {@inheritDoc} + */ + public function doDownload(PackageInterface $package, $path) + { + $ref = $package->getSourceReference(); + $label = $package->getPrettyVersion(); + + $this->io->write(' Cloning ' . $ref); + $this->initPerforce($package, $path, $ref); + $this->perforce->setStream($ref); + $this->perforce->p4Login($this->io); + $this->perforce->writeP4ClientSpec(); + $this->perforce->connectClient(); + $this->perforce->syncCodeBase($label); + $this->perforce->cleanupClientSpec(); + } + + private function initPerforce($package, $path, $ref) + { + if ($this->perforceInjected) { + return; + } + $repository = $package->getRepository(); + $repoConfig = null; + if ($repository instanceof VcsRepository) { + $repoConfig = $this->getRepoConfig($repository); + } + $this->perforce = Perforce::createPerforce($repoConfig, $package->getSourceUrl(), $path); + } + + private function getRepoConfig(VcsRepository $repository) + { + return $repository->getRepoConfig(); + } + + /** + * {@inheritDoc} + */ + public function doUpdate(PackageInterface $initial, PackageInterface $target, $path) + { + $this->doDownload($target, $path); + } + + /** + * {@inheritDoc} + */ + public function getLocalChanges(PackageInterface $package, $path) + { + $this->io->write('Perforce driver does not check for local changes before overriding', true); + return; + } + + + /** + * {@inheritDoc} + */ + protected function getCommitLogs($fromReference, $toReference, $path) + { + $commitLogs = $this->perforce->getCommitLogs($fromReference, $toReference); + return $commitLogs; + } + + public function injectPerforce($perforce) + { + $this->perforce = $perforce; + $this->perforceInjected = true; + } +} diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 6875bbc4f..1f794786b 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -355,6 +355,7 @@ class Factory $dm->setDownloader('git', new Downloader\GitDownloader($io, $config)); $dm->setDownloader('svn', new Downloader\SvnDownloader($io, $config)); $dm->setDownloader('hg', new Downloader\HgDownloader($io, $config)); + $dm->setDownloader('perforce', new Downloader\PerforceDownloader($io, $config)); $dm->setDownloader('zip', new Downloader\ZipDownloader($io, $config, $eventDispatcher, $cache)); $dm->setDownloader('rar', new Downloader\RarDownloader($io, $config, $eventDispatcher, $cache)); $dm->setDownloader('tar', new Downloader\TarDownloader($io, $config, $eventDispatcher, $cache)); diff --git a/src/Composer/Repository/Vcs/PerforceDriver.php b/src/Composer/Repository/Vcs/PerforceDriver.php new file mode 100644 index 000000000..03acd0a21 --- /dev/null +++ b/src/Composer/Repository/Vcs/PerforceDriver.php @@ -0,0 +1,190 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + + +namespace Composer\Repository\Vcs; + +use Composer\IO\IOInterface; +use Composer\Util\ProcessExecutor; +use Composer\Util\Filesystem; +use Composer\Util\Perforce; + +/** + * @author Matt Whittom + */ +class PerforceDriver extends VcsDriver +{ + protected $depot; + protected $branch; + protected $perforce; + protected $composerInfo; + protected $composerInfoIdentifier; + + /** + * {@inheritDoc} + */ + public function initialize() + { + $this->depot = $this->repoConfig['depot']; + $this->branch = ''; + if (isset($this->repoConfig['branch'])) { + $this->branch = $this->repoConfig['branch']; + } + + $this->initPerforce($this->repoConfig); + $this->perforce->p4Login($this->io); + $this->perforce->checkStream($this->depot); + + $this->perforce->writeP4ClientSpec(); + $this->perforce->connectClient(); + + return true; + } + + private function initPerforce($repoConfig) + { + if (isset($this->perforce)) { + return; + } + + $repoDir = $this->config->get('cache-vcs-dir') . '/' . $this->depot; + $this->perforce = Perforce::createPerforce($repoConfig, $this->getUrl(), $repoDir, $this->process); + } + + /** + * {@inheritDoc} + */ + public function getComposerInformation($identifier) + { + if (isset($this->composerInfoIdentifier)) { + if (strcmp($identifier, $this->composerInfoIdentifier) === 0) { + return $this->composerInfo; + } + } + $composer_info = $this->perforce->getComposerInformation($identifier); + + return $composer_info; + } + + /** + * {@inheritDoc} + */ + public function getRootIdentifier() + { + return $this->branch; + } + + /** + * {@inheritDoc} + */ + public function getBranches() + { + $branches = $this->perforce->getBranches(); + + return $branches; + } + + /** + * {@inheritDoc} + */ + public function getTags() + { + $tags = $this->perforce->getTags(); + + return $tags; + } + + /** + * {@inheritDoc} + */ + public function getDist($identifier) + { + return null; + } + + /** + * {@inheritDoc} + */ + public function getSource($identifier) + { + $source = array( + 'type' => 'perforce', + 'url' => $this->repoConfig['url'], + 'reference' => $identifier, + 'p4user' => $this->perforce->getUser() + ); + + return $source; + } + + /** + * {@inheritDoc} + */ + public function getUrl() + { + return $this->url; + } + + /** + * {@inheritDoc} + */ + public function hasComposerFile($identifier) + { + $this->composerInfo = $this->perforce->getComposerInformation('//' . $this->depot . '/' . $identifier); + $this->composerInfoIdentifier = $identifier; + $result = false; + if (isset($this->composerInfo)) { + $result = count($this->composerInfo) > 0; + } + return $result; + } + + /** + * {@inheritDoc} + */ + public function getContents($url) + { + return false; + } + + /** + * {@inheritDoc} + */ + public static function supports(IOInterface $io, $url, $deep = false) + { + return Perforce::checkServerExists($url, new ProcessExecutor); + } + + /** + * {@inheritDoc} + */ + public function cleanup() + { + $this->perforce->cleanupClientSpec(); + $this->perforce = null; + } + + public function getDepot() + { + return $this->depot; + } + + public function getBranch() + { + return $this->branch; + } + + public function injectPerforce(Perforce $perforce) + { + $this->perforce = $perforce; + } +} diff --git a/src/Composer/Repository/Vcs/VcsDriver.php b/src/Composer/Repository/Vcs/VcsDriver.php index 84b209bd3..6f49d5b82 100644 --- a/src/Composer/Repository/Vcs/VcsDriver.php +++ b/src/Composer/Repository/Vcs/VcsDriver.php @@ -110,4 +110,12 @@ abstract class VcsDriver implements VcsDriverInterface { return (bool) preg_match('{^(file://|/|[a-z]:[\\\\/])}i', $url); } + + /** + * {@inheritDoc} + */ + public function cleanup() + { + return; + } } diff --git a/src/Composer/Repository/Vcs/VcsDriverInterface.php b/src/Composer/Repository/Vcs/VcsDriverInterface.php index 44486f007..b29841b68 100644 --- a/src/Composer/Repository/Vcs/VcsDriverInterface.php +++ b/src/Composer/Repository/Vcs/VcsDriverInterface.php @@ -81,6 +81,12 @@ interface VcsDriverInterface */ public function hasComposerFile($identifier); + /** + * Performs any cleanup necessary as the driver is not longer needed + * + */ + public function cleanup(); + /** * Checks if this driver can handle a given url * diff --git a/src/Composer/Repository/VcsRepository.php b/src/Composer/Repository/VcsRepository.php index f8fb84005..701db33bb 100644 --- a/src/Composer/Repository/VcsRepository.php +++ b/src/Composer/Repository/VcsRepository.php @@ -46,6 +46,7 @@ class VcsRepository extends ArrayRepository 'git' => 'Composer\Repository\Vcs\GitDriver', 'hg-bitbucket' => 'Composer\Repository\Vcs\HgBitbucketDriver', 'hg' => 'Composer\Repository\Vcs\HgDriver', + 'perforce' => 'Composer\Repository\Vcs\PerforceDriver', // svn must be last because identifying a subversion server for sure is practically impossible 'svn' => 'Composer\Repository\Vcs\SvnDriver', ); @@ -58,6 +59,11 @@ class VcsRepository extends ArrayRepository $this->repoConfig = $repoConfig; } + public function getRepoConfig() + { + return $this->repoConfig; + } + public function setLoader(LoaderInterface $loader) { $this->loader = $loader; @@ -247,6 +253,7 @@ class VcsRepository extends ArrayRepository continue; } } + $driver->cleanup(); if (!$verbose) { $this->io->overwrite('', false); diff --git a/src/Composer/Util/Perforce.php b/src/Composer/Util/Perforce.php new file mode 100644 index 000000000..2fb0e4bea --- /dev/null +++ b/src/Composer/Util/Perforce.php @@ -0,0 +1,518 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + + +namespace Composer\Util; + +use Composer\IO\IOInterface; +use Symfony\Component\Process\Process; + +/** + * @author Matt Whittom + */ +class Perforce +{ + + protected $path; + protected $p4Depot; + protected $p4Client; + protected $p4User; + protected $p4Password; + protected $p4Port; + protected $p4Stream; + protected $p4ClientSpec; + protected $p4DepotType; + protected $p4Branch; + protected $process; + protected $uniquePerforceClientName; + protected $windowsFlag; + + + public static function createPerforce($repoConfig, $port, $path, ProcessExecutor $process = null) + { + if (!isset($process)) { + $process = new ProcessExecutor; + } + $isWindows = defined('PHP_WINDOWS_VERSION_BUILD'); + + $perforce = new Perforce($repoConfig, $port, $path, $process, $isWindows); + return $perforce; + } + + public function __construct($repoConfig, $port, $path, ProcessExecutor $process, $isWindows) + { + $this->windowsFlag = $isWindows; + $this->p4Port = $port; + $this->path = $path; + $fs = new Filesystem(); + $fs->ensureDirectoryExists($path); + $this->process = $process; + $this->initialize($repoConfig); + } + + public function initialize($repoConfig) + { + $this->uniquePerforceClientName = $this->generateUniquePerforceClientName(); + if (null == $repoConfig) { + return; + } + if (isset($repoConfig['unique_perforce_client_name'])) { + $this->uniquePerforceClientName = $repoConfig['unique_perforce_client_name']; + } + + if (isset($repoConfig['depot'])) { + $this->p4Depot = $repoConfig['depot']; + } + if (isset($repoConfig['branch'])) { + $this->p4Branch = $repoConfig['branch']; + } + if (isset($repoConfig['p4user'])) { + $this->p4User = $repoConfig['p4user']; + } else { + $this->p4User = $this->getP4variable('P4USER'); + } + if (isset($repoConfig['p4password'])) { + $this->p4Password = $repoConfig['p4password']; + } + } + + public function initializeDepotAndBranch($depot, $branch) + { + if (isset($depot)) { + $this->p4Depot = $depot; + } + if (isset($branch)) { + $this->p4Branch = $branch; + } + } + + public function generateUniquePerforceClientName() + { + return gethostname() . "_" . time(); + } + + public function cleanupClientSpec() + { + $client = $this->getClient(); + $command = 'p4 client -d $client'; + $this->executeCommand($command); + $clientSpec = $this->getP4ClientSpec(); + $fileSystem = new FileSystem($this->process); + $fileSystem->remove($clientSpec); + } + + protected function executeCommand($command) + { + $result = ""; + $this->process->execute($command, $result); + + return $result; + } + + public function getClient() + { + if (!isset($this->p4Client)) { + $cleanStreamName = str_replace('@', '', str_replace('/', '_', str_replace('//', '', $this->getStream()))); + $this->p4Client = 'composer_perforce_' . $this->uniquePerforceClientName . '_' . $cleanStreamName; + } + + return $this->p4Client; + } + + protected function getPath() + { + return $this->path; + } + + protected function getPort() + { + return $this->p4Port; + } + + public function setStream($stream) + { + $this->p4Stream = $stream; + $index = strrpos($stream, '/'); + //Stream format is //depot/stream, while non-streaming depot is //depot + if ($index > 2) { + $this->p4DepotType = 'stream'; + } + } + + public function isStream() + { + return (strcmp($this->p4DepotType, 'stream') === 0); + } + + public function getStream() + { + if (!isset($this->p4Stream)) { + if ($this->isStream()) { + $this->p4Stream = '//' . $this->p4Depot . '/' . $this->p4Branch; + } else { + $this->p4Stream = '//' . $this->p4Depot; + } + } + return $this->p4Stream; + } + + public function getStreamWithoutLabel($stream) + { + $index = strpos($stream, '@'); + if ($index === false) { + return $stream; + } + + return substr($stream, 0, $index); + } + + public function getP4ClientSpec() + { + $p4clientSpec = $this->path . '/' . $this->getClient() . '.p4.spec'; + + return $p4clientSpec; + } + + public function getUser() + { + return $this->p4User; + } + + public function queryP4User(IOInterface $io) + { + $this->getUser(); + if (strlen($this->p4User) > 0) { + return; + } + $this->p4User = $this->getP4variable('P4USER'); + if (strlen($this->p4User) > 0) { + return; + } + $this->p4User = $io->ask('Enter P4 User:'); + if ($this->windowsFlag) { + $command = 'p4 set P4USER=' . $this->p4User; + } else { + $command = 'export P4USER=' . $this->p4User; + } + $result = $this->executeCommand($command); + } + + protected function getP4variable($name) + { + if ($this->windowsFlag) { + $command = 'p4 set'; + $result = $this->executeCommand($command); + $resArray = explode(PHP_EOL, $result); + foreach ($resArray as $line) { + $fields = explode('=', $line); + if (strcmp($name, $fields[0]) == 0) { + $index = strpos($fields[1], ' '); + if ($index === false) { + $value = $fields[1]; + } else { + $value = substr($fields[1], 0, $index); + } + $value = trim($value); + + return $value; + } + } + } else { + $command = 'echo $' . $name; + $result = trim($this->executeCommand($command)); + + return $result; + } + } + + public function queryP4Password(IOInterface $io) + { + if (isset($this->p4Password)) { + return $this->p4Password; + } + $password = $this->getP4variable('P4PASSWD'); + if (strlen($password) <= 0) { + $password = $io->askAndHideAnswer('Enter password for Perforce user ' . $this->getUser() . ': '); + } + $this->p4Password = $password; + + return $password; + } + + public function generateP4Command($command, $useClient = true) + { + $p4Command = 'p4 '; + $p4Command = $p4Command . '-u ' . $this->getUser() . ' '; + if ($useClient) { + $p4Command = $p4Command . '-c ' . $this->getClient() . ' '; + } + $p4Command = $p4Command . '-p ' . $this->getPort() . ' '; + $p4Command = $p4Command . $command; + + return $p4Command; + } + + public function isLoggedIn() + { + $command = $this->generateP4Command('login -s', false); + $result = trim($this->executeCommand($command)); + $index = strpos($result, $this->getUser()); + if ($index === false) { + return false; + } + return true; + } + + public function connectClient() + { + $p4CreateClientCommand = $this->generateP4Command('client -i < ' . $this->getP4ClientSpec()); + $this->executeCommand($p4CreateClientCommand); + } + + public function syncCodeBase($label) + { + $prevDir = getcwd(); + chdir($this->path); + + $p4SyncCommand = $this->generateP4Command('sync -f '); + if (isset($label)) { + if (strcmp($label, 'dev-master') != 0) { + $p4SyncCommand = $p4SyncCommand . '@' . $label; + } + } + $result = $this->executeCommand($p4SyncCommand); + + chdir($prevDir); + } + + public function writeClientSpecToFile($spec) + { + fwrite($spec, 'Client: ' . $this->getClient() . PHP_EOL . PHP_EOL); + fwrite($spec, 'Update: ' . date('Y/m/d H:i:s') . PHP_EOL . PHP_EOL); + fwrite($spec, 'Access: ' . date('Y/m/d H:i:s') . PHP_EOL); + fwrite($spec, 'Owner: ' . $this->getUser() . PHP_EOL . PHP_EOL); + fwrite($spec, 'Description:' . PHP_EOL); + fwrite($spec, ' Created by ' . $this->getUser() . ' from composer.' . PHP_EOL . PHP_EOL); + fwrite($spec, 'Root: ' . $this->getPath() . PHP_EOL . PHP_EOL); + fwrite($spec, 'Options: noallwrite noclobber nocompress unlocked modtime rmdir' . PHP_EOL . PHP_EOL); + fwrite($spec, 'SubmitOptions: revertunchanged' . PHP_EOL . PHP_EOL); + fwrite($spec, 'LineEnd: local' . PHP_EOL . PHP_EOL); + if ($this->isStream()) { + fwrite($spec, 'Stream:' . PHP_EOL); + fwrite($spec, ' ' . $this->getStreamWithoutLabel($this->p4Stream) . PHP_EOL); + } else { + fwrite( + $spec, + 'View: ' . $this->getStream() . '/... //' . $this->getClient() . '/... ' . PHP_EOL + ); + } + } + + public function writeP4ClientSpec() + { + $clientSpec = $this->getP4ClientSpec(); + $spec = fopen($clientSpec, 'w'); + try { + $this->writeClientSpecToFile($spec); + } catch (\Exception $e) { + fclose($spec); + throw $e; + } + fclose($spec); + } + + + protected function read($pipe, $name) + { + if (feof($pipe)) { + return; + } + $line = fgets($pipe); + while ($line != false) { + $line = fgets($pipe); + } + + return; + } + + public function windowsLogin($password) + { + $command = $this->generateP4Command(' login -a'); + $process = new Process($command, null, null, $password); + return $process->run(); + } + + + public function p4Login(IOInterface $io) + { + $this->queryP4User($io); + if (!$this->isLoggedIn()) { + $password = $this->queryP4Password($io); + if ($this->windowsFlag) { + $this->windowsLogin($password); + } else { + $command = 'echo ' . $password . ' | ' . $this->generateP4Command(' login -a', false); + $this->executeCommand($command); + } + } + } + + public static function checkServerExists($url, ProcessExecutor $processExecutor) + { + $result = ''; + $processExecutor->execute('p4 -p ' . $url . ' info -s', $result); + return false === strpos($result, 'error'); + } + + public function getComposerInformation($identifier) + { + $index = strpos($identifier, '@'); + if ($index === false) { + $composerJson = $identifier. '/composer.json'; + + return $this->getComposerInformationFromPath($composerJson); + } + return $this->getComposerInformationFromLabel($identifier, $index); + } + + public function getComposerInformationFromPath($composerJson) + { + $command = $this->generateP4Command(' print ' . $composerJson); + $result = $this->executeCommand($command); + $index = strpos($result, '{'); + if ($index === false) { + return ''; + } + if ($index >= 0) { + $rawData = substr($result, $index); + $composer_info = json_decode($rawData, true); + + return $composer_info; + } + + return ''; + } + + public function getComposerInformationFromLabel($identifier, $index) + { + $composerJsonPath = substr($identifier, 0, $index) . '/composer.json' . substr($identifier, $index); + $command = $this->generateP4Command(' files ' . $composerJsonPath, false); + $result = $this->executeCommand($command); + $index2 = strpos($result, 'no such file(s).'); + if ($index2 === false) { + $index3 = strpos($result, 'change'); + if (!($index3 === false)) { + $phrase = trim(substr($result, $index3)); + $fields = explode(' ', $phrase); + $id = $fields[1]; + $composerJson = substr($identifier, 0, $index) . '/composer.json@' . $id; + + return $this->getComposerInformationFromPath($composerJson); + } + } + + return ""; + } + + public function getBranches() + { + $possibleBranches = array(); + if (!$this->isStream()) { + $possibleBranches[$this->p4Branch] = $this->getStream(); + } else { + $command = $this->generateP4Command('streams //' . $this->p4Depot . '/...'); + $result = $this->executeCommand($command); + $resArray = explode(PHP_EOL, $result); + foreach ($resArray as $line) { + $resBits = explode(' ', $line); + if (count($resBits) > 4) { + $branch = preg_replace('/[^A-Za-z0-9 ]/', '', $resBits[4]); + $possibleBranches[$branch] = $resBits[1]; + } + } + } + $branches = array(); + $branches['master'] = $possibleBranches[$this->p4Branch]; + + return $branches; + } + + public function getTags() + { + $command = $this->generateP4Command('labels'); + $result = $this->executeCommand($command); + $resArray = explode(PHP_EOL, $result); + $tags = array(); + foreach ($resArray as $line) { + $index = strpos($line, 'Label'); + if (!($index === false)) { + $fields = explode(' ', $line); + $tags[$fields[1]] = $this->getStream() . '@' . $fields[1]; + } + } + + return $tags; + } + + public function checkStream() + { + $command = $this->generateP4Command('depots', false); + $result = $this->executeCommand($command); + $resArray = explode(PHP_EOL, $result); + foreach ($resArray as $line) { + $index = strpos($line, 'Depot'); + if (!($index === false)) { + $fields = explode(' ', $line); + if (strcmp($this->p4Depot, $fields[1]) === 0) { + $this->p4DepotType = $fields[3]; + + return $this->isStream(); + } + } + } + + return false; + } + + protected function getChangeList($reference) + { + $index = strpos($reference, '@'); + if ($index === false) { + return; + } + $label = substr($reference, $index); + $command = $this->generateP4Command(' changes -m1 ' . $label); + $changes = $this->executeCommand($command); + if (strpos($changes, 'Change') !== 0) { + return; + } + $fields = explode(' ', $changes); + $changeList = $fields[1]; + return $changeList; + } + + public function getCommitLogs($fromReference, $toReference) + { + $fromChangeList = $this->getChangeList($fromReference); + if ($fromChangeList == null) { + return; + } + $toChangeList = $this->getChangeList($toReference); + if ($toChangeList == null) { + return; + } + $index = strpos($fromReference, '@'); + $main = substr($fromReference, 0, $index) . '/...'; + $command = $this->generateP4Command('filelog ' . $main . '@' . $fromChangeList. ',' . $toChangeList); + $result = $this->executeCommand($command); + return $result; + } +} diff --git a/tests/Composer/Test/Downloader/PerforceDownloaderTest.php b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php new file mode 100644 index 000000000..b247cfbf3 --- /dev/null +++ b/tests/Composer/Test/Downloader/PerforceDownloaderTest.php @@ -0,0 +1,108 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + + +namespace Composer\Test\Downloader; + +use Composer\Downloader\PerforceDownloader; +use Composer\Config; +use Composer\Repository\VcsRepository; + +/** + * @author Matt Whittom + */ +class PerforceDownloaderTest extends \PHPUnit_Framework_TestCase +{ + + private $io; + private $config; + private $testPath; + public static $repository; + + protected function setUp() + { + $this->testPath = sys_get_temp_dir() . '/composer-test'; + $this->config = new Config(); + $this->config->merge( + array( + 'config' => array( + 'home' => $this->testPath, + ), + ) + ); + $this->io = $this->getMock('Composer\IO\IOInterface'); + } + + + public function testDoDownloadGetRepoConfig() + { + $downloader = new PerforceDownloader($this->io, $this->config); + $package = $this->getMock('Composer\Package\PackageInterface'); + $repoConfig = array('url' => 'TEST_URL', 'p4user' => 'TEST_USER'); + $repository = $this->getMock( + 'Composer\Repository\VcsRepository', + array('getRepoConfig'), + array($repoConfig, $this->io, $this->config) + ); + $package->expects($this->at(0)) + ->method('getSourceReference') + ->will($this->returnValue('SOURCE_REF')); + $package->expects($this->at(1)) + ->method('getPrettyVersion') + ->will($this->returnValue('100')); + $package->expects($this->at(2)) + ->method('getRepository') + ->will($this->returnValue($repository)); + $repository->expects($this->at(0)) + ->method('getRepoConfig'); + $path = $this->testPath; + $downloader->doDownload($package, $path); + } + + public function testDoDownload() + { + $downloader = new PerforceDownloader($this->io, $this->config); + $repoConfig = array('depot' => 'TEST_DEPOT', 'branch' => 'TEST_BRANCH', 'p4user' => 'TEST_USER'); + $port = 'TEST_PORT'; + $path = 'TEST_PATH'; + $process = $this->getmock('Composer\Util\ProcessExecutor'); + $perforce = $this->getMock( + 'Composer\Util\Perforce', + array('setStream', 'queryP4User', 'writeP4ClientSpec', 'connectClient', 'syncCodeBase'), + array($repoConfig, $port, $path, $process, true, 'TEST') + ); + $ref = 'SOURCE_REF'; + $label = 'LABEL'; + $perforce->expects($this->at(0)) + ->method('setStream') + ->with($this->equalTo($ref)); + $perforce->expects($this->at(1)) + ->method('queryP4User') + ->with($this->io); + $perforce->expects($this->at(2)) + ->method('writeP4ClientSpec'); + $perforce->expects($this->at(3)) + ->method('connectClient'); + $perforce->expects($this->at(4)) + ->method('syncCodeBase') + ->with($this->equalTo($label)); + $downloader->injectPerforce($perforce); + $package = $this->getMock('Composer\Package\PackageInterface'); + $package->expects($this->at(0)) + ->method('getSourceReference') + ->will($this->returnValue($ref)); + $package->expects($this->at(1)) + ->method('getPrettyVersion') + ->will($this->returnValue($label)); + $path = $this->testPath; + $downloader->doDownload($package, $path); + } +} diff --git a/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php new file mode 100644 index 000000000..5b0bc43f9 --- /dev/null +++ b/tests/Composer/Test/Repository/Vcs/PerforceDriverTest.php @@ -0,0 +1,134 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer\Test\Repository\Vcs; + + +use Composer\Repository\Vcs\PerforceDriver; +use Composer\Util\Filesystem; +use Composer\Config; + +/** + * @author Matt Whittom + */ +class PerforceDriverTest extends \PHPUnit_Framework_TestCase +{ + private $config; + private $io; + private $process; + private $remoteFileSystem; + private $testPath; + + public function setUp() + { + $this->testPath = sys_get_temp_dir() . '/composer-test'; + $this->config = new Config(); + $this->config->merge( + array( + 'config' => array( + 'home' => $this->testPath, + ), + ) + ); + + $this->io = $this->getMock('Composer\IO\IOInterface'); + $this->process = $this->getMock('Composer\Util\ProcessExecutor'); + $this->remoteFileSystem = $this->getMockBuilder('Composer\Util\RemoteFilesystem')->disableOriginalConstructor() + ->getMock(); + } + + public function tearDown() + { + $fs = new Filesystem; + $fs->removeDirectory($this->testPath); + } + + public function testInitializeCapturesVariablesFromRepoConfig() + { + $this->setUp(); + $repoConfig = array( + 'url' => 'TEST_PERFORCE_URL', + 'depot' => 'TEST_DEPOT_CONFIG', + 'branch' => 'TEST_BRANCH_CONFIG' + ); + $driver = new PerforceDriver($repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); + $process = $this->getMock('Composer\Util\ProcessExecutor'); + $arguments = array( + array('depot' => 'TEST_DEPOT', 'branch' => 'TEST_BRANCH'), + 'port' => 'TEST_PORT', + 'path' => $this->testPath, + $process, + true, + 'TEST' + ); + $perforce = $this->getMock('Composer\Util\Perforce', null, $arguments); + $driver->injectPerforce($perforce); + $driver->initialize(); + $this->assertEquals('TEST_PERFORCE_URL', $driver->getUrl()); + $this->assertEquals('TEST_DEPOT_CONFIG', $driver->getDepot()); + $this->assertEquals('TEST_BRANCH_CONFIG', $driver->getBranch()); + } + + public function testInitializeLogsInAndConnectsClient() + { + $this->setUp(); + $repoConfig = array( + 'url' => 'TEST_PERFORCE_URL', + 'depot' => 'TEST_DEPOT_CONFIG', + 'branch' => 'TEST_BRANCH_CONFIG' + ); + $driver = new PerforceDriver($repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); + $perforce = $this->getMockBuilder('Composer\Util\Perforce')->disableOriginalConstructor()->getMock(); + $perforce->expects($this->at(0)) + ->method('p4Login') + ->with($this->io); + $perforce->expects($this->at(1)) + ->method('checkStream') + ->with($this->equalTo('TEST_DEPOT_CONFIG')); + $perforce->expects($this->at(2)) + ->method('writeP4ClientSpec'); + $perforce->expects($this->at(3)) + ->method('connectClient'); + + $driver->injectPerforce($perforce); + $driver->initialize(); + } + + public function testHasComposerFile() + { + $this->setUp(); + $repoConfig = array( + 'url' => 'TEST_PERFORCE_URL', + 'depot' => 'TEST_DEPOT_CONFIG', + 'branch' => 'TEST_BRANCH_CONFIG' + ); + $driver = new PerforceDriver($repoConfig, $this->io, $this->config, $this->process, $this->remoteFileSystem); + $process = $this->getMock('Composer\Util\ProcessExecutor'); + $arguments = array( + array('depot' => 'TEST_DEPOT', 'branch' => 'TEST_BRANCH'), + 'port' => 'TEST_PORT', + 'path' => $this->testPath, + $process, + true, + 'TEST' + ); + $perforce = $this->getMock('Composer\Util\Perforce', array('getComposerInformation'), $arguments); + $perforce->expects($this->at(0)) + ->method('getComposerInformation') + ->with($this->equalTo('//TEST_DEPOT_CONFIG/TEST_IDENTIFIER')) + ->will($this->returnValue('Some json stuff')); + $driver->injectPerforce($perforce); + $driver->initialize(); + $identifier = 'TEST_IDENTIFIER'; + $result = $driver->hasComposerFile($identifier); + $this->assertTrue($result); + } +} diff --git a/tests/Composer/Test/Util/PerforceTest.php b/tests/Composer/Test/Util/PerforceTest.php new file mode 100644 index 000000000..6bde3d797 --- /dev/null +++ b/tests/Composer/Test/Util/PerforceTest.php @@ -0,0 +1,676 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + + +namespace Composer\Test\Util; + +use Composer\Util\Perforce; +use Composer\Util\ProcessExecutor; + +/** + * @author Matt Whittom + */ +class PerforceTest extends \PHPUnit_Framework_TestCase +{ + + protected $perforce; + protected $processExecutor; + + public function setUp() + { + $this->processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); + $repoConfig = array( + 'depot' => 'depot', + 'branch' => 'branch', + 'p4user' => 'user', + 'unique_perforce_client_name' => 'TEST' + ); + $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true); + } + + public function testGetClientWithoutStream() + { + $client = $this->perforce->getClient(); + $hostname = gethostname(); + $timestamp = time(); + + $expected = 'composer_perforce_TEST_depot'; + $this->assertEquals($expected, $client); + } + + public function testGetClientFromStream() + { + $this->setPerforceToStream(); + + $client = $this->perforce->getClient(); + + $expected = 'composer_perforce_TEST_depot_branch'; + $this->assertEquals($expected, $client); + } + + public function testGetStreamWithoutStream() + { + $stream = $this->perforce->getStream(); + $this->assertEquals("//depot", $stream); + } + + public function testGetStreamWithStream() + { + $this->setPerforceToStream(); + + $stream = $this->perforce->getStream(); + $this->assertEquals('//depot/branch', $stream); + } + + + public function testGetStreamWithoutLabelWithStreamWithoutLabel() + { + $stream = $this->perforce->getStreamWithoutLabel('//depot/branch'); + $this->assertEquals('//depot/branch', $stream); + } + + public function testGetStreamWithoutLabelWithStreamWithLabel() + { + $stream = $this->perforce->getStreamWithoutLabel('//depot/branching@label'); + $this->assertEquals('//depot/branching', $stream); + } + + public function testGetClientSpec() + { + $clientSpec = $this->perforce->getP4ClientSpec(); + $expected = 'path/composer_perforce_TEST_depot.p4.spec'; + $this->assertEquals($expected, $clientSpec); + } + + public function testGenerateP4Command() + { + $command = 'do something'; + $p4Command = $this->perforce->generateP4Command($command); + $expected = 'p4 -u user -c composer_perforce_TEST_depot -p port do something'; + $this->assertEquals($expected, $p4Command); + } + + public function testQueryP4UserWithUserAlreadySet() + { + $io = $this->getMock('Composer\IO\IOInterface'); + + $repoConfig = array('depot' => 'depot', 'branch' => 'branch', 'p4user' => 'TEST_USER'); + $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true, 'TEST'); + + $this->perforce->queryP4user($io); + $this->assertEquals('TEST_USER', $this->perforce->getUser()); + } + + public function testQueryP4UserWithUserSetInP4VariablesWithWindowsOS() + { + $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); + $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true, 'TEST'); + + $io = $this->getMock('Composer\IO\IOInterface'); + $expectedCommand = 'p4 set'; + $this->processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will( + $this->returnCallback( + function ($command, &$output) { + $output = 'P4USER=TEST_P4VARIABLE_USER' . PHP_EOL ; + return true; + } + ) + ); + + $this->perforce->queryP4user($io); + $this->assertEquals('TEST_P4VARIABLE_USER', $this->perforce->getUser()); + } + + public function testQueryP4UserWithUserSetInP4VariablesNotWindowsOS() + { + $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); + $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); + + $io = $this->getMock('Composer\IO\IOInterface'); + $expectedCommand = 'echo $P4USER'; + $this->processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will( + $this->returnCallback( + function ($command, &$output) { + $output = 'TEST_P4VARIABLE_USER' . PHP_EOL; + return true; + } + ) + ); + + $this->perforce->queryP4user($io); + $this->assertEquals('TEST_P4VARIABLE_USER', $this->perforce->getUser()); + } + + public function testQueryP4UserQueriesForUser() + { + $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); + $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); + $io = $this->getMock('Composer\IO\IOInterface'); + $expectedQuestion = 'Enter P4 User:'; + $io->expects($this->at(0)) + ->method('ask') + ->with($this->equalTo($expectedQuestion)) + ->will($this->returnValue('TEST_QUERY_USER')); + + $this->perforce->queryP4user($io); + $this->assertEquals('TEST_QUERY_USER', $this->perforce->getUser()); + } + + public function testQueryP4UserStoresResponseToQueryForUserWithWindows() + { + $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); + $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, true, 'TEST'); + + $io = $this->getMock('Composer\IO\IOInterface'); + $expectedQuestion = 'Enter P4 User:'; + $io->expects($this->at(0)) + ->method('ask') + ->with($this->equalTo($expectedQuestion)) + ->will($this->returnValue('TEST_QUERY_USER')); + $expectedCommand = 'p4 set P4USER=TEST_QUERY_USER'; + $this->processExecutor->expects($this->at(1)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnValue(0)); + + $this->perforce->queryP4user($io); + } + + public function testQueryP4UserStoresResponseToQueryForUserWithoutWindows() + { + $repoConfig = array('depot' => 'depot', 'branch' => 'branch'); + $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); + + $io = $this->getMock('Composer\IO\IOInterface'); + $expectedQuestion = 'Enter P4 User:'; + $io->expects($this->at(0)) + ->method('ask') + ->with($this->equalTo($expectedQuestion)) + ->will($this->returnValue('TEST_QUERY_USER')); + $expectedCommand = 'export P4USER=TEST_QUERY_USER'; + $this->processExecutor->expects($this->at(1)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnValue(0)); + + $this->perforce->queryP4user($io); + } + + public function testQueryP4PasswordWithPasswordAlreadySet() + { + $repoConfig = array( + 'depot' => 'depot', + 'branch' => 'branch', + 'p4user' => 'user', + 'p4password' => 'TEST_PASSWORD' + ); + $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); + $io = $this->getMock('Composer\IO\IOInterface'); + + $password = $this->perforce->queryP4Password($io); + $this->assertEquals('TEST_PASSWORD', $password); + } + + public function testQueryP4PasswordWithPasswordSetInP4VariablesWithWindowsOS() + { + $io = $this->getMock('Composer\IO\IOInterface'); + + $expectedCommand = 'p4 set'; + $this->processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will( + $this->returnCallback( + function ($command, &$output) { + $output = 'P4PASSWD=TEST_P4VARIABLE_PASSWORD' . PHP_EOL; + return true; + } + ) + ); + + $password = $this->perforce->queryP4Password($io); + $this->assertEquals('TEST_P4VARIABLE_PASSWORD', $password); + } + + public function testQueryP4PasswordWithPasswordSetInP4VariablesNotWindowsOS() + { + $repoConfig = array('depot' => 'depot', 'branch' => 'branch', 'p4user' => 'user'); + $this->perforce = new Perforce($repoConfig, 'port', 'path', $this->processExecutor, false, 'TEST'); + + $io = $this->getMock('Composer\IO\IOInterface'); + $expectedCommand = 'echo $P4PASSWD'; + $this->processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will( + $this->returnCallback( + function ($command, &$output) { + $output = 'TEST_P4VARIABLE_PASSWORD' . PHP_EOL; + return true; + } + ) + ); + + $password = $this->perforce->queryP4Password($io); + $this->assertEquals('TEST_P4VARIABLE_PASSWORD', $password); + } + + public function testQueryP4PasswordQueriesForPassword() + { + $io = $this->getMock('Composer\IO\IOInterface'); + $expectedQuestion = 'Enter password for Perforce user user: '; + $io->expects($this->at(0)) + ->method('askAndHideAnswer') + ->with($this->equalTo($expectedQuestion)) + ->will($this->returnValue('TEST_QUERY_PASSWORD')); + + $password = $this->perforce->queryP4Password($io); + $this->assertEquals('TEST_QUERY_PASSWORD', $password); + } + + public function testWriteP4ClientSpecWithoutStream() + { + $stream = fopen('php://memory', 'w+'); + $this->perforce->writeClientSpecToFile($stream); + + rewind($stream); + + $expectedArray = $this->getExpectedClientSpec(false); + try { + foreach ($expectedArray as $expected) { + $this->assertStringStartsWith($expected, fgets($stream)); + } + $this->assertFalse(fgets($stream)); + } catch (Exception $e) { + fclose($stream); + throw $e; + } + fclose($stream); + } + + public function testWriteP4ClientSpecWithStream() + { + $this->setPerforceToStream(); + $stream = fopen('php://memory', 'w+'); + + $this->perforce->writeClientSpecToFile($stream); + rewind($stream); + + $expectedArray = $this->getExpectedClientSpec(true); + try { + foreach ($expectedArray as $expected) { + $this->assertStringStartsWith($expected, fgets($stream)); + } + $this->assertFalse(fgets($stream)); + } catch (Exception $e) { + fclose($stream); + throw $e; + } + fclose($stream); + } + + public function testIsLoggedIn() + { + $expectedCommand = 'p4 -u user -p port login -s'; + $this->processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand), $this->equalTo(null)) + ->will($this->returnValue(0)); + + $this->perforce->isLoggedIn(); + } + + public function testConnectClient() + { + $expectedCommand = 'p4 -u user -c composer_perforce_TEST_depot -p port client -i < path/composer_perforce_TEST_depot.p4.spec'; + $this->processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand), $this->equalTo(null)) + ->will($this->returnValue(0)); + + $this->perforce->connectClient(); + } + + public function testGetBranchesWithStream() + { + $this->setPerforceToStream(); + + $expectedCommand = 'p4 -u user -c composer_perforce_TEST_depot_branch -p port streams //depot/...'; + $this->processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will( + $this->returnCallback( + function ($command, &$output) { + $output = 'Stream //depot/branch mainline none \'branch\'' . PHP_EOL; + return true; + } + ) + ); + + $branches = $this->perforce->getBranches(); + $this->assertEquals('//depot/branch', $branches['master']); + } + + public function testGetBranchesWithoutStream() + { + $branches = $this->perforce->getBranches(); + $this->assertEquals('//depot', $branches['master']); + } + + public function testGetTagsWithoutStream() + { + $expectedCommand = 'p4 -u user -c composer_perforce_TEST_depot -p port labels'; + $this->processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will( + $this->returnCallback( + function ($command, &$output) { + $output = 'Label 0.0.1 2013/07/31 \'First Label!\'' . PHP_EOL . 'Label 0.0.2 2013/08/01 \'Second Label!\'' . PHP_EOL; + return true; + } + ) + ); + + $tags = $this->perforce->getTags(); + $this->assertEquals('//depot@0.0.1', $tags['0.0.1']); + $this->assertEquals('//depot@0.0.2', $tags['0.0.2']); + } + + public function testGetTagsWithStream() + { + $this->setPerforceToStream(); + + $expectedCommand = 'p4 -u user -c composer_perforce_TEST_depot_branch -p port labels'; + $this->processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will( + $this->returnCallback( + function ($command, &$output) { + $output = 'Label 0.0.1 2013/07/31 \'First Label!\'' . PHP_EOL . 'Label 0.0.2 2013/08/01 \'Second Label!\'' . PHP_EOL; + return true; + } + ) + ); + + $tags = $this->perforce->getTags(); + $this->assertEquals('//depot/branch@0.0.1', $tags['0.0.1']); + $this->assertEquals('//depot/branch@0.0.2', $tags['0.0.2']); + } + + public function testCheckStreamWithoutStream() + { + $result = $this->perforce->checkStream('depot'); + $this->assertFalse($result); + $this->assertFalse($this->perforce->isStream()); + } + + public function testCheckStreamWithStream() + { + $this->processExecutor->expects($this->any())->method('execute') + ->will( + $this->returnCallback( + function ($command, &$output) { + $output = 'Depot depot 2013/06/25 stream /p4/1/depots/depot/... \'Created by Me\''; + return true; + } + ) + ); + $result = $this->perforce->checkStream('depot'); + $this->assertTrue($result); + $this->assertTrue($this->perforce->isStream()); + } + + public function testGetComposerInformationWithoutLabelWithoutStream() + { + $expectedCommand = 'p4 -u user -c composer_perforce_TEST_depot -p port print //depot/composer.json'; + $this->processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will( + $this->returnCallback( + function ($command, &$output) { + $output = PerforceTest::getComposerJson(); + return true; + } + ) + ); + + $result = $this->perforce->getComposerInformation('//depot'); + $expected = array( + 'name' => 'test/perforce', + 'description' => 'Basic project for testing', + 'minimum-stability' => 'dev', + 'autoload' => array('psr-0' => array()) + ); + $this->assertEquals($expected, $result); + } + + public function testGetComposerInformationWithLabelWithoutStream() + { + $expectedCommand = 'p4 -u user -p port files //depot/composer.json@0.0.1'; + $this->processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will( + $this->returnCallback( + function ($command, &$output) { + $output = '//depot/composer.json#1 - branch change 10001 (text)'; + return true; + } + ) + ); + + $expectedCommand = 'p4 -u user -c composer_perforce_TEST_depot -p port print //depot/composer.json@10001'; + $this->processExecutor->expects($this->at(1)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will( + $this->returnCallback( + function ($command, &$output) { + $output = PerforceTest::getComposerJson(); + return true; + } + ) + ); + + $result = $this->perforce->getComposerInformation('//depot@0.0.1'); + + $expected = array( + 'name' => 'test/perforce', + 'description' => 'Basic project for testing', + 'minimum-stability' => 'dev', + 'autoload' => array('psr-0' => array()) + ); + $this->assertEquals($expected, $result); + } + + public function testGetComposerInformationWithoutLabelWithStream() + { + $this->setPerforceToStream(); + + $expectedCommand = 'p4 -u user -c composer_perforce_TEST_depot_branch -p port print //depot/branch/composer.json'; + $this->processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will( + $this->returnCallback( + function ($command, &$output) { + $output = PerforceTest::getComposerJson(); + return true; + } + ) + ); + + $result = $this->perforce->getComposerInformation('//depot/branch'); + + $expected = array( + 'name' => 'test/perforce', + 'description' => 'Basic project for testing', + 'minimum-stability' => 'dev', + 'autoload' => array('psr-0' => array()) + ); + $this->assertEquals($expected, $result); + } + + public function testGetComposerInformationWithLabelWithStream() + { + $this->setPerforceToStream(); + $expectedCommand = 'p4 -u user -p port files //depot/branch/composer.json@0.0.1'; + $this->processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will( + $this->returnCallback( + function ($command, &$output) { + $output = '//depot/composer.json#1 - branch change 10001 (text)'; + return true; + } + ) + ); + + $expectedCommand = 'p4 -u user -c composer_perforce_TEST_depot_branch -p port print //depot/branch/composer.json@10001'; + $this->processExecutor->expects($this->at(1)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will( + $this->returnCallback( + function ($command, &$output) { + $output = PerforceTest::getComposerJson(); + return true; + } + ) + ); + + $result = $this->perforce->getComposerInformation('//depot/branch@0.0.1'); + + $expected = array( + 'name' => 'test/perforce', + 'description' => 'Basic project for testing', + 'minimum-stability' => 'dev', + 'autoload' => array('psr-0' => array()) + ); + $this->assertEquals($expected, $result); + } + + public function testSyncCodeBaseWithoutStream() + { + $expectedCommand = 'p4 -u user -c composer_perforce_TEST_depot -p port sync -f @label'; + $this->processExecutor->expects($this->at(1)) + ->method('execute') + ->with($this->equalTo($expectedCommand), $this->equalTo(null)) + ->will($this->returnValue(0)); + + $this->perforce->syncCodeBase('label'); + } + + public function testSyncCodeBaseWithStream() + { + $this->setPerforceToStream(); + $expectedCommand = 'p4 -u user -c composer_perforce_TEST_depot_branch -p port sync -f @label'; + $this->processExecutor->expects($this->at(1)) + ->method('execute') + ->with($this->equalTo($expectedCommand)) + ->will($this->returnValue(0)); + + $this->perforce->syncCodeBase('label'); + } + + public function testCheckServerExists() + { + $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); + + $expectedCommand = 'p4 -p perforce.does.exist:port info -s'; + $processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand), $this->equalTo(null)) + ->will($this->returnValue(0)); + + $result = $this->perforce->checkServerExists('perforce.does.exist:port', $processExecutor); + $this->assertTrue($result); + } + + public function testCheckServerExistsWithFailure() + { + $processExecutor = $this->getMock('Composer\Util\ProcessExecutor'); + + $expectedCommand = 'p4 -p perforce.does.not.exist:port info -s'; + $processExecutor->expects($this->at(0)) + ->method('execute') + ->with($this->equalTo($expectedCommand), $this->equalTo(null)) + ->will($this->returnValue('Perforce client error:')); + + $result = $this->perforce->checkServerExists('perforce.does.not.exist:port', $processExecutor); + $this->assertTrue($result); + } + + public static function getComposerJson() + { + $composer_json = array( + '{', + '"name": "test/perforce",', + '"description": "Basic project for testing",', + '"minimum-stability": "dev",', + '"autoload": {', + '"psr-0" : {', + '}', + '}', + '}' + ); + + return implode($composer_json); + } + + private function getExpectedClientSpec($withStream) + { + $expectedArray = array( + 'Client: composer_perforce_TEST_depot', + PHP_EOL, + 'Update:', + PHP_EOL, + 'Access:', + 'Owner: user', + PHP_EOL, + 'Description:', + ' Created by user from composer.', + PHP_EOL, + 'Root: path', + PHP_EOL, + 'Options: noallwrite noclobber nocompress unlocked modtime rmdir', + PHP_EOL, + 'SubmitOptions: revertunchanged', + PHP_EOL, + 'LineEnd: local', + PHP_EOL + ); + if ($withStream) { + $expectedArray[] = 'Stream:'; + $expectedArray[] = ' //depot/branch'; + } else { + $expectedArray[] = 'View: //depot/... //composer_perforce_TEST_depot/...'; + } + + return $expectedArray; + } + + private function setPerforceToStream() + { + $this->perforce->setStream('//depot/branch'); + } +}