<?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 ); } }