<?php
/**
* Test the perl trigger script.
*
* @copyright 2015 Perforce Software. All rights reserved.
* @license Please see LICENSE.txt in top-level folder of this distribution.
* @version <release>/<patch>
*/
namespace ModuleTest;
use P4\File\File;
use P4\Spec\Change;
use Reviews\Model\Review;
class PerlTriggerTest extends BashTriggerTest
{
protected $scriptBasename = 'swarm-trigger.pl';
/**
* Test exempt files count configurable for strict/enforce triggers (with a change under review).
*/
public function testExemptFileCountWithReview()
{
$this->installTriggers(
array(
array('job', 'job', array()),
array('user', 'user', array()),
array('userdel', 'user', array()),
array('group', 'group', array()),
array('groupdel', 'group', array()),
array('enforce', '//...', array()),
array('strict', '//...', array())
)
);
// create a change to review
$change = new Change($this->p4);
$change->setDescription('test');
// add files
for ($i = 0; $i < 2; $i++) {
$file = new File($this->p4);
$file->setFilespec('//depot/a' . $i)
->open(null, 'text')
->setLocalContents('abc' . $i);
$change->addFile($file);
}
$change->save();
// create a review
$this->p4->run('shelve', array('-c', $change->getId()));
$review = Review::createFromChange($change, $this->p4)
->save()
->updateFromChange($change)
->save();
// at the first attempt, modify files and try to submit with no files exempt count set,
// it should fail
$change->revert();
for ($i = 0; $i < 4; $i++) {
$file = new File($this->p4);
$file->setFilespec('//depot/a' . $i)
->open(null, 'text')
->setLocalContents('xyz' . $i);
$change->addFile($file);
}
$change->save();
// approve the review (otherwise commit will fail)
$review->setState(Review::STATE_APPROVED)->save();
// delete shelved files before committing
$this->p4->run('shelve', array('-d', '-c', $change->getId()));
// commit the modified change and verify the output
try {
$change->submit();
} catch (\P4\Connection\Exception\CommandException $e) {
// if trigger prevents committing the change, this is the exception class thrown
}
$this->assertTrue(isset($e));
// second attempt, set exempt files count (lower than number of files in change)
// and try again, it should succeed
unset($e);
$this->configureScript(array('EXEMPT_FILE_COUNT' => 3));
try {
$change->submit();
} catch (\P4\Connection\Exception\CommandException $e) {
// if trigger prevents committing the change, this is the exception class thrown
}
$this->assertFalse(isset($e));
}
/**
* Test exempt files count configurable for strict/enforce triggers (with change not under review).
*/
public function testExemptFileCountWithNoReview()
{
$this->installTriggers(
array(
array('job', 'job', array()),
array('user', 'user', array()),
array('userdel', 'user', array()),
array('group', 'group', array()),
array('groupdel', 'group', array()),
array('enforce', '//...', array()),
array('strict', '//...', array())
)
);
// create a change to submit
$change = new Change($this->p4);
$change->setDescription('test');
// add 3 files
for ($i = 0; $i < 3; $i++) {
$file = new File($this->p4);
$file->setFilespec('//depot/a' . $i)
->open(null, 'text')
->setLocalContents('abc' . $i);
$change->addFile($file);
}
$change->save();
// commit the change and verify the output
try {
$change->submit();
} catch (\P4\Connection\Exception\CommandException $e) {
// if trigger prevents committing the change, this is the exception class thrown
}
$this->assertTrue(isset($e));
// second attempt, set exempt files count (lower than number of files in change)
// and try again, it should succeed
unset($e);
$this->configureScript(array('EXEMPT_FILE_COUNT' => 2));
try {
$change->submit();
} catch (\P4\Connection\Exception\CommandException $e) {
// if trigger prevents committing the change, this is the exception class thrown
}
$this->assertFalse(isset($e));
}
/**
* Test strict/enforce triggers in conjunction with EXEMPT_EXTENSIONS config option (with change under review).
*/
public function testExemptExtensionsWithReview()
{
$this->configureScript();
$this->installTriggers(
array(
array('job', 'job', array()),
array('user', 'user', array()),
array('userdel', 'user', array()),
array('group', 'group', array()),
array('groupdel', 'group', array()),
array('enforce', '//...', array()),
array('strict', '//...', array())
)
);
// create a change to review
$change = new Change($this->p4);
$change->setDescription('test');
// add files
for ($i = 0; $i < 3; $i++) {
$file = new File($this->p4);
$file->setFilespec('//depot/a' . $i)
->open(null, 'text')
->setLocalContents('abc' . $i);
$change->addFile($file);
}
$change->save();
// create a review
$this->p4->run('shelve', array('-c', $change->getId()));
$review = Review::createFromChange($change, $this->p4)
->save()
->updateFromChange($change)
->save();
// at the first attempt, modify files and try to submit with no exempt extensions
// specified, it should fail
$change->revert();
$file1 = new File($this->p4);
$file1->setFilespec('//depot/foo.ext1')
->open(null, 'text')
->setLocalContents('abc');
$file2 = new File($this->p4);
$file2->setFilespec('//depot/bar.ext2')
->open(null, 'text')
->setLocalContents('xyz');
$change->addFile($file1)->addFile($file2)->save();
// approve the review
$review->setState(Review::STATE_APPROVED)->save();
// delete shelved files before committing
$this->p4->run('shelve', array('-d', '-c', $change->getId()));
// commit the modified change and verify the output
try {
$change->submit();
} catch (\P4\Connection\Exception\CommandException $e) {
// if trigger prevents committing the change, this is the exception class thrown
}
$errorMessage = 'content of this change (1) does not match the content of the associated Swarm review (2)';
$this->assertTrue(isset($e));
$this->assertTrue(stripos($e->getMessage(), $errorMessage) !== false);
// second attempt, set exemopt extensions to 'ext1', it still should fail
unset($e);
$this->configureScript(array('EXEMPT_EXTENSIONS' => 'ext1'));
try {
$change->submit();
} catch (\P4\Connection\Exception\CommandException $e) {
// if trigger prevents committing the change, this is the exception class thrown
}
$this->assertTrue(isset($e));
$this->assertTrue(stripos($e->getMessage(), $errorMessage) !== false);
// third attempt - verify literal dot appears before the extension
$file = new File($this->p4);
$file->setFilespec('//depot/foo-ext1')
->open(null, 'text')
->setLocalContents('xyz');
$change->setFiles(array($file))->save();
unset($e);
$this->configureScript(array('EXEMPT_EXTENSIONS' => 'ext1'));
try {
$change->submit();
} catch (\P4\Connection\Exception\CommandException $e) {
// if trigger prevents committing the change, this is the exception class thrown
}
$this->assertTrue(isset($e));
$this->assertTrue(stripos($e->getMessage(), $errorMessage) !== false);
// fourth attempt, set exempt extensions to 'ext1, ext2, ext3', it should succeed
$change->setFiles(array($file1, $file2))->save();
unset($e);
$this->configureScript(array('EXEMPT_EXTENSIONS' => '.EXT1, .ext2,ext3'));
try {
$change->submit();
} catch (\P4\Connection\Exception\CommandException $e) {
// if trigger prevents committing the change, this is the exception class thrown
print $e->getMessage();
exit;
}
$this->assertFalse(isset($e));
}
/**
* Test strict/enforce triggers in conjunction with EXEMPT_EXTENSIONS config option (with change not under review).
*/
public function testExemptExtensionsWithNoReview()
{
$this->configureScript();
$this->installTriggers(
array(
array('job', 'job', array()),
array('user', 'user', array()),
array('userdel', 'user', array()),
array('group', 'group', array()),
array('groupdel', 'group', array()),
array('enforce', '//...', array()),
array('strict', '//...', array())
)
);
// create a change and few files
$change = new Change($this->p4);
$change->setDescription('test');
$file1 = new File($this->p4);
$file1->setFilespec('//depot/foo.ext1')
->open(null, 'text')
->setLocalContents('abc');
$file2 = new File($this->p4);
$file2->setFilespec('//depot/bar.ext2')
->open(null, 'text')
->setLocalContents('xyz');
// at the first attempt, try to submit with no exempt extensions specified, it should fail
$change->addFile($file1)->addFile($file2)->save();
// commit the change and verify the output
try {
$change->submit();
} catch (\P4\Connection\Exception\CommandException $e) {
// if trigger prevents committing the change, this is the exception class thrown
}
$errorMessage = 'Cannot find a Swarm review associated with this change (1)';
$this->assertTrue(isset($e));
$this->assertTrue(stripos($e->getMessage(), $errorMessage) !== false);
// second attempt, set exempt extensions to 'ext1', it still should fail
unset($e);
$this->configureScript(array('EXEMPT_EXTENSIONS' => 'ext1'));
try {
$change->submit();
} catch (\P4\Connection\Exception\CommandException $e) {
// if trigger prevents committing the change, this is the exception class thrown
}
$this->assertTrue(isset($e));
$this->assertTrue(stripos($e->getMessage(), $errorMessage) !== false);
// third attempt - verify literal dot appears before the extension
$file = new File($this->p4);
$file->setFilespec('//depot/foo-ext1')
->open(null, 'text')
->setLocalContents('xyz');
$change->setFiles(array($file))->save();
unset($e);
$this->configureScript(array('EXEMPT_EXTENSIONS' => 'ext1'));
try {
$change->submit();
} catch (\P4\Connection\Exception\CommandException $e) {
// if trigger prevents committing the change, this is the exception class thrown
}
$this->assertTrue(isset($e));
$this->assertTrue(stripos($e->getMessage(), $errorMessage) !== false);
// fourth attempt, set exempt extensions to 'ext1, ext2, ext3', it should succeed
$change->setFiles(array($file1, $file2))->save();
unset($e);
$this->configureScript(array('EXEMPT_EXTENSIONS' => '.EXT1, .ext2,ext3'));
try {
$change->submit();
} catch (\P4\Connection\Exception\CommandException $e) {
// if trigger prevents committing the change, this is the exception class thrown
print $e->getMessage();
exit;
}
$this->assertFalse(isset($e));
}
/**
* @dataProvider strictTriggerWithKtextFilesProvider
*/
public function testStrictTriggerWithKtextFiles(array $reviewFiles, array $submitFiles, $shouldFail)
{
$this->configureScript();
$this->installTriggers(
array(
array('job', 'job', array()),
array('user', 'user', array()),
array('userdel', 'user', array()),
array('group', 'group', array()),
array('groupdel', 'group', array()),
array('enforce', '//...', array()),
array('strict', '//...', array())
)
);
// create a change to review
$change = new Change($this->p4);
$change->setDescription('strict test');
// add files as specified
foreach ($reviewFiles as $fileSpec => $properties) {
$content = isset($properties['content']) ? $properties['content'] : '';
$type = isset($properties['type']) ? $properties['type'] : null;
$file = new File($this->p4);
$file->setFilespec($fileSpec)
->open(null, $type)
->setLocalContents($content);
$change->addFile($file);
}
$change->save();
// create a review from the change
$this->p4->run('shelve', array('-c', $change->getId()));
$review = Review::createFromChange($change, $this->p4)
->save()
->updateFromChange($change)
->save();
// modify a change (that is now associated with a review) with files as specified and try to commit it
$change->revert();
foreach ($submitFiles as $fileSpec => $properties) {
$content = isset($properties['content']) ? $properties['content'] : '';
$type = isset($properties['type']) ? $properties['type'] : null;
$file = new File($this->p4);
$file->setFilespec($fileSpec)
->open(null, $type)
->setLocalContents($content);
$change->addFile($file);
}
$change->save();
// approve the review (otherwise commit will fail)
$review->setState(Review::STATE_APPROVED)->save();
// delete shelved files before committing
$this->p4->run('shelve', array('-d', '-c', $change->getId()));
// commit the modified change and verify the output
try {
$change->submit();
} catch (\P4\Connection\Exception\CommandException $e) {
// if trigger prevents committing the change, this is the exception class thrown
// we verify the message later
}
// verify the output
if ($shouldFail) {
$this->assertTrue(isset($e));
$this->assertTrue(
strpos(
$e->getMessage(),
"The content of this change (1) does not match the content of the associated Swarm review (2)"
) !== false
);
} else {
// should it not fail, ensure we didn't catch any exception
$this->assertFalse(isset($e), "Unexpected trigger behaviour (should not fail).");
}
}
protected function getExpectedUsage($help = false)
{
$usage = parent::getExpectedUsage();
// perl trigger has a short version of usage unless help output requested
return $help ? $usage : current(explode("\nThis script is", $usage));
}
protected function adjustTriggerScript(
$targetFilename,
array $configPaths,
array $verifyLines,
array $deleteLines,
$insertAfterLine
) {
// we were called with values for bash trigger script, we need to change some
// of them here according to the perl trigger script
$offset = 688;
$verifyLines = array(
$offset => 'sub parse_config {',
$offset + 1 => ' my @candidates = (',
$offset + 4 => ' "$MY_PATH/swarm-trigger.conf"'
);
$deleteLines = array($offset + 2, $offset + 3);
$insertAfterLine = $offset + 1;
// remove trailing \ from config paths and add punctuation at the end
$configPaths = array_map(
function ($path) {
return rtrim($path, ' \\') . ',';
},
$configPaths
);
return parent::adjustTriggerScript(
$targetFilename,
$configPaths,
$verifyLines,
$deleteLines,
$insertAfterLine
);
}
}