- <?php
- /**
- * Test methods for the P4 Change class.
- *
- * @copyright 2012 Perforce Software. All rights reserved.
- * @license Please see LICENSE.txt in top-level folder of this distribution.
- * @version <release>/<patch>
- */
-
- namespace P4Test\Spec;
-
- use P4Test\TestCase;
- use P4\Connection\Connection;
- use P4\Connection\Exception\CommandException;
- use P4\Connection\Exception\ConflictException;
- use P4\File\File;
- use P4\File\Query as FileQuery;
- use P4\Log\Logger;
- use P4\Model\Fielded\Iterator as FieldedIterator;
- use P4\Spec\Client;
- use P4\Spec\Change;
- use P4\Spec\Definition;
- use P4\Spec\Exception\NotFoundException;
- use P4\Spec\Exception\UnopenedException;
- use P4\Spec\Exception\Exception as SpecException;
- use P4\Spec\Job;
- use P4\Spec\User;
- use Zend\Log\Logger as ZendLogger;
- use Zend\Log\Writer\Mock as MockLog;
-
- class ChangeTest extends TestCase
- {
- /**
- * Test instantiation.
- */
- public function testInstantiation()
- {
- $change = new Change;
- $this->assertTrue($change instanceof Change, 'Expected class');
- $originalConnection = $change->getConnection();
-
- // test setting connection via constructor.
- $p4 = Connection::factory();
- $change = new Change($p4);
- $this->assertSame(
- $p4,
- $change->getConnection(),
- 'getConnection() should match connection passed to constructor.'
- );
-
- // ensure two connections differ.
- $this->assertNotSame(
- $change->getConnection(),
- $originalConnection,
- 'Connections should not match.'
- );
- }
-
- /**
- * Test exists.
- */
- public function testExists()
- {
- // ensure id-exists returns false for non-existant change
- // (we have no changes yet)
- $this->assertFalse(Change::exists(123), 'Given change should not exist.');
-
- // test that a bogus id does not exist
- $this->assertFalse(Change::exists('*'), 'Bogus change should not exist.');
-
- // ensure default change exists.
- $this->assertTrue(Change::exists('default'), 'Default change should exist.');
-
- // create a change and ensure it exists.
- $change = new Change;
- $change->setDescription('this is a test');
- $change->save();
-
- // new change should have id of 1.
- $this->assertTrue($change->getId() === 1, 'Change number should be one.');
-
- // change number 1 should exist.
- $this->assertTrue(Change::exists(1), 'Change 1 should exist.');
- $this->assertTrue(Change::exists('1'), 'Change "1" should exist.');
- }
-
- /**
- * Test fetch.
- */
- public function testFetch()
- {
- // ensure fetch fails for a non-existant change.
- try {
- Change::fetch(1234);
- $this->fail('Fetch should fail for a non-existant change.');
- } catch (NotFoundException $e) {
- $this->assertTrue(true);
- }
-
- // ensure fetch succeeds for default change.
- try {
- Change::fetch('default');
- $this->assertTrue(true);
- } catch (NotFoundException $e) {
- $this->fail('Fetch should succeed for default change.');
- }
-
- // ensure fetch succeeds for numbered change.
- $change = new Change;
- $description = "this is a test\n";
- $change->setDescription($description);
- $change->save();
- try {
- $fetched = Change::fetch($change->getId());
- $this->assertTrue(true);
- } catch (NotFoundException $e) {
- $this->fail('Fetch should succeed for a numbered change that exists.');
- }
-
- // ensure it only takes one command to fetch a change and read
- // basic values from it -- we verify this by peeking at the log
- $original = Logger::hasLogger() ? Logger::getLogger() : null;
- $logger = new ZendLogger;
- $mock = new MockLog;
- $logger->addWriter($mock);
- Logger::setLogger($logger);
-
- $fetched = Change::fetch($change->getId());
- $this->assertSame($description, $fetched->getDescription());
- $this->assertInternalType('int', $fetched->getTime());
- $this->assertSame(1, count($mock->events));
-
- // restore original logger if there is one.
- Logger::setLogger($original);
- }
-
- /**
- * Test that there is no difference between a saved change and a fetched change.
- *
- * @todo test get files.
- */
- public function testSavedVsFetched()
- {
- // open a file for add.
- $file = new File;
- $file->setFilespec('//depot/test-file')
- ->add();
-
- // create a job.
- $job = new Job;
- $job->set('Description', 'fix something')
- ->save();
-
- // save a change with a file and a job.
- $change = new Change;
- $change->setDescription("a change with a file and a job.\n")
- ->setFiles(array($file->getFilespec()))
- ->setJobs(array($job->getId()))
- ->save();
-
- $fetched = Change::fetch($change->getId());
-
- $types = array('Id', 'Date', 'User', 'Status', 'Description', 'JobStatus', 'Jobs');
- foreach ($types as $type) {
- $method = "get$type";
- $this->assertSame($fetched->$method(), $change->$method(), "Expect matching $type.");
- }
- }
-
- /**
- * Test the fetch all method.
- */
- public function testFetchAll()
- {
- /*
- * FETCH_MAXIMUM - set to integer value to limit to the
- * first 'max' number of entries.
- * FETCH_BY_FILESPEC - set to a filespec to limit changes to those
- * affecting the file(s) matching the filespec.
- * FETCH_BY_STATUS - set to a valid change status to limit result
- * to changes with that status (e.g. 'pending').
- * FETCH_INTEGRATED - set to true to include changes integrated
- * into the specified files.
- * FETCH_BY_CLIENT - set to a client to limit changes to those
- * on the named client.
- * FETCH_BY_USER - set to a user to limit changes to those
- * owned by the named user.
- * FETCH_AFTER - set to an id _after_ which to start collecting entries
- * note: entries seen before 'after' count towards max.
- */
-
- // create a file and submitted change
- $file1 = new File;
- $file1->setFilespec('//depot/path-a/test-file')->add()->setLocalContents('test')->submit('test-1');
-
- // create a file and submitted change
- $file2 = new File;
- $file2->setFilespec('//depot/path-b/test-file')->add()->setLocalContents('test')->submit('test-2');
-
- // create a pending change with 2 files
- $files = new FieldedIterator;
- $file3 = new File;
- $files[] = $file3->setFilespec('//depot/path-c/test-file1')
- ->add()
- ->setLocalContents('test');
- $file4 = new File;
- $files[] = $file4->setFilespec('//depot/path-c/test-file2')
- ->add()
- ->setLocalContents('test');
- $change = new Change;
- $change->setFiles($files)->setDescription("Has 2 files\n")->save();
-
- // create a change by another user, in another workspace.
- $user = new User;
- $password = 'AnotherPass';
- $user->setId('alternate')
- ->setEmail('alternate@example.perforce.com')
- ->setFullName('Alternate User')
- ->save();
- $client = new Client;
- $clientId = 'another-test-client';
- $client->setId($clientId)
- ->setRoot(DATA_PATH . "/clients/$clientId")
- ->setView(array('//depot/... //another-test-client/...'))
- ->save();
- $p4 = Connection::factory(
- $this->p4->getPort(),
- $user->getId(),
- $client->getId()
- );
- $change = new Change($p4);
- $change->setDescription("in alternate client\n")->save();
-
- // and have the alternate user integrate an existing file
- $integFilespec = '//depot/path-a/test-integ';
- $result = $p4->run('integrate', array('-f', $file1->getFilespec(), $integFilespec));
-
- $change = new Change($p4);
- $change->addFile($integFilespec)->setDescription('Integration')->submit();
-
- // Testing begins: ensure correct number of changes returned.
- $changes = Change::fetchAll();
- $this->assertEquals(
- 5,
- $changes->count(),
- 'There should be 5 changes.'
- );
-
- // ensure that first change matches last submitted change.
- $expected = array(
- 'Change' => 5,
- 'Client' => $clientId,
- 'User' => $user->getId(),
- 'Status' => 'submitted',
- 'Description' => "Integration\n",
- 'Type' => 'public',
- 'JobStatus' => null,
- 'Jobs' => array()
- );
- $actual = $changes->first()->get();
- unset($actual['Date'], $actual['Files']);
- $this->assertEquals(
- $expected,
- $actual,
- 'Fetched change should match expected values.'
- );
-
- // battery of tests against this setup with various options
- $tests = array(
- array(
- 'label' => __LINE__ .': defaults',
- 'options' => array(),
- 'expected' => array(
- "Integration\n",
- "in alternate client\n",
- "Has 2 files\n",
- "test-2",
- "test-1"
- ),
- ),
-
- array(
- 'label' => __LINE__ .': fetch maximum 1',
- 'options' => array(
- Change::FETCH_MAXIMUM => 1,
- ),
- 'expected' => array(
- "Integration\n",
- ),
- ),
- array(
- 'label' => __LINE__ .': fetch maximum 2',
- 'options' => array(
- Change::FETCH_MAXIMUM => 2,
- ),
- 'expected' => array(
- "Integration\n",
- "in alternate client\n",
- ),
- ),
- array(
- 'label' => __LINE__ .': fetch maximum 3',
- 'options' => array(
- Change::FETCH_MAXIMUM => 3,
- ),
- 'expected' => array(
- "Integration\n",
- "in alternate client\n",
- "Has 2 files\n",
- ),
- ),
- array(
- 'label' => __LINE__ .': fetch maximum 4',
- 'options' => array(
- Change::FETCH_MAXIMUM => 4,
- ),
- 'expected' => array(
- "Integration\n",
- "in alternate client\n",
- "Has 2 files\n",
- "test-2",
- ),
- ),
- array(
- 'label' => __LINE__ .': fetch maximum 5',
- 'options' => array(
- Change::FETCH_MAXIMUM => 5,
- ),
- 'expected' => array(
- "Integration\n",
- "in alternate client\n",
- "Has 2 files\n",
- "test-2",
- "test-1"
- ),
- ),
- array(
- 'label' => __LINE__ .': fetch maximum 6',
- 'options' => array(
- Change::FETCH_MAXIMUM => 6,
- ),
- 'expected' => array(
- "Integration\n",
- "in alternate client\n",
- "Has 2 files\n",
- "test-2",
- "test-1"
- ),
- ),
- array(
- 'label' => __LINE__ .': fetch maximum 0',
- 'options' => array(
- Change::FETCH_MAXIMUM => 0,
- ),
- 'expected' => array(
- "Integration\n",
- "in alternate client\n",
- "Has 2 files\n",
- "test-2",
- "test-1"
- ),
- ),
-
- array(
- 'label' => __LINE__ .': fetch by filespec //depot/.../test-file',
- 'options' => array(
- Change::FETCH_BY_FILESPEC => '//depot/.../test-file',
- ),
- 'expected' => array(
- "test-2",
- "test-1"
- ),
- ),
-
- array(
- 'label' => __LINE__ .': fetch by status submitted',
- 'options' => array(
- Change::FETCH_BY_STATUS => 'submitted',
- ),
- 'expected' => array(
- "Integration\n",
- "test-2",
- "test-1"
- ),
- ),
- array(
- 'label' => __LINE__ .': fetch by status pending',
- 'options' => array(
- Change::FETCH_BY_STATUS => 'pending',
- ),
- 'expected' => array(
- "in alternate client\n",
- "Has 2 files\n",
- ),
- ),
-
- array(
- 'label' => __LINE__ .': fetch by client regular',
- 'options' => array(
- Change::FETCH_BY_CLIENT => $this->p4->getClient(),
- ),
- 'expected' => array(
- "Has 2 files\n",
- "test-2",
- "test-1"
- ),
- ),
- array(
- 'label' => __LINE__ .': fetch by client alternate',
- 'options' => array(
- Change::FETCH_BY_CLIENT => $clientId,
- ),
- 'expected' => array(
- "Integration\n",
- "in alternate client\n",
- ),
- ),
-
- array(
- 'label' => __LINE__ .': fetch by user regular',
- 'options' => array(
- Change::FETCH_BY_USER => $this->p4->getUser(),
- ),
- 'expected' => array(
- "Has 2 files\n",
- "test-2",
- "test-1"
- ),
- ),
- array(
- 'label' => __LINE__ .': fetch by user alternate',
- 'options' => array(
- Change::FETCH_BY_USER => $user->getId(),
- ),
- 'expected' => array(
- "Integration\n",
- "in alternate client\n",
- ),
- ),
-
- array(
- 'label' => __LINE__ .': fetch without integrated',
- 'options' => array(
- Change::FETCH_INTEGRATED => false,
- Change::FETCH_BY_FILESPEC => $integFilespec,
- ),
- 'expected' => array(
- "Integration\n",
- ),
- ),
- array(
- 'label' => __LINE__ .': fetch with integrated',
- 'options' => array(
- Change::FETCH_INTEGRATED => true,
- Change::FETCH_BY_FILESPEC => $integFilespec,
- ),
- 'expected' => array(
- "Integration\n",
- "test-1"
- ),
- ),
- array(
- 'label' => __LINE__ .': fetch with after',
- 'options' => array(
- Change::FETCH_AFTER => 4
- ),
- 'expected' => array(
- "Has 2 files\n",
- 'test-2',
- 'test-1'
- ),
- ),
- );
-
- foreach ($tests as $test) {
- $label = $test['label'];
- $changes = Change::fetchAll($test['options']);
- $this->assertEquals(
- count($test['expected']),
- count($changes),
- "$label - Expected change count."
- );
- $descriptions = array();
- foreach ($changes as $change) {
- $descriptions[] = $change->getDescription();
- }
- $this->assertSame(
- $test['expected'],
- $descriptions,
- "$label - Expected change descriptions."
- );
- }
-
- }
-
- /**
- * Test getJobs and setJobs.
- */
- public function testAddGetSetJobs()
- {
- // test initial state of jobs in a fresh change object.
- $change = new Change;
- $jobs = $change->getJobs();
- $this->assertTrue(is_array($jobs), 'Expect an array from getJobs.');
- $this->assertSame(0, count($jobs), 'There should be no jobs associated with a fresh change.');
-
- // create a job
- $job = new Job;
- $job->set('Description', 'This is job #1');
- $job->save();
-
- // create a change to associate with the job
- $change = new Change;
- $change->setJobs(array($job->getId()));
- $jobs = $change->getJobs();
- $this->assertSame(1, count($jobs), 'Expect one job.');
- $this->assertSame($job->getId(), $jobs[0], 'Expect matching job id.');
-
- // add another job
- $extraJobId = 'anotherJob';
- $change->addJob($extraJobId);
- $jobs = $change->getJobs();
- $this->assertSame(2, count($jobs), 'Expect two jobs.');
- $this->assertSame($job->getId(), $jobs[0], 'Expect matching job id.');
- $this->assertSame($extraJobId, $jobs[1], 'Expect matching job id for added job.');
-
- // attempt to save the change with a non-existant job
- try {
- $change->setDescription('A change with jobs.')
- ->save('save the change');
- $this->fail('Unexpected success saving change with a non-existant job.');
- } catch (CommandException $e) {
- $this->assertRegexp(
- "/Job 'anotherJob' doesn't exist\./",
- $e->getMessage(),
- 'Expected error saving change with a non-existant job.'
- );
- } catch (\Exception $e) {
- $this->fail(
- 'Unexpected exception saving change with a non-existant job ('
- . get_class($e) .') '. $e->getMessage()
- );
- }
-
- // create the non-existant job.
- $job2 = new Job;
- $job2->set('Description', 'Was non-existant')
- ->setId($extraJobId)
- ->save();
-
- // re-attempt saving the change now that the job exists.
- $change->setDescription('Change with jobs.')
- ->save();
-
- // fetch the changes we have, and verify the jobs.
- $changes = Change::fetchAll();
- $this->assertEquals(1, count($changes), 'Expect one change.');
- $this->assertSame(2, count($changes->first()->getJobs()), 'Expect 2 jobs with fetched change.');
- $this->assertSame(
- array($extraJobId, $job->getId()),
- $changes->first()->getJobs(),
- 'Expected jobs in fetched change.'
- );
- $this->assertTrue($changes->first()->isPending(), 'Change should be pending.');
- $this->assertFalse($changes->first()->isSubmitted(), 'Change should not be submitted.');
-
- // now submit the change, and check the jobs.
- $file = new File;
- $file->setFilespec('//depot/file.txt')->add()->setLocalContents('File content.');
- $change->addFile($file);
- $change->submit();
-
- $changes = Change::fetchAll();
- $this->assertEquals(1, count($changes), 'Expect one change.');
- $this->assertSame(2, count($changes->first()->getJobs()), 'Expect 2 jobs with fetched & submitted change.');
- $this->assertSame(
- array($extraJobId, $job->getId()),
- $changes->first()->getJobs(),
- 'Expected jobs in fetched & submitted change.'
- );
- $this->assertFalse($changes->first()->isPending(), 'Change should not be pending.');
- $this->assertTrue($changes->first()->isSubmitted(), 'Change should be submitted.');
-
- // create a new change, with non-existant jobId, and submit it.
- $change = new Change;
- $change->setDescription('Try submitting with non-existant job.');
- $thirdJobId = 'yetAnotherJob';
- $change->addJob($thirdJobId);
- $file = new File;
- $file->setFileSpec('//depot/file2.txt')->add()->setLocalContents('File content.');
- $change->addFile($file);
- $this->assertSame(null, $change->getId(), 'Expect id prior to submit.');
-
- try {
- $change->submit('Attempt 1.');
- $this->fail('Unexpected success submitting change with a non-existant job.');
- } catch (CommandException $e) {
- $this->assertRegexp(
- "/Change 2 created.*Job '$thirdJobId' doesn't exist\./s",
- $e->getMessage(),
- 'Expected error message submitting change with a non-existant job.'
- );
- } catch (\Exception $e) {
- $this->fail(
- 'Unexpected exception submitting change with a non-existant job ('
- . get_class($e) .') '. $e->getMessage()
- );
- }
- $this->assertSame(2, $change->getId(), 'Expected id after submit.');
-
- // create the job
- $job3 = new Job;
- $job3->set('Description', 'Was non-existant')
- ->setId($thirdJobId)
- ->save();
-
- // submit should now succeed.
- $change->submit('Attempt 2.');
- $changes = Change::fetchAll();
- $this->assertEquals(2, count($changes), 'Expect two changes.');
- $this->assertEquals(1, count($changes->first()->getJobs()), 'Expect 1 job with second submitted change.');
- $this->assertSame(
- array($thirdJobId),
- $changes->first()->getJobs(),
- 'Expected jobs in second submitted change.'
- );
- }
-
- /**
- * Test functionality of the setFixStatus() method.
- */
- public function testFixStatus()
- {
- // modify job spec to add custom statuses
- $spec = Definition::fetch('job');
- $fields = $spec->getFields();
- $fields['Status']['options'] = array('a', 'b', 'c', 'd', 'closed');
- $fields['Status']['default'] = 'a';
- $spec->setFields($fields)->save();
-
- // open a file for add
- $file = new File;
- $file->setFilespec("//depot/foo")->setLocalContents("this is a test")->add();
-
- // create couple of jobs
- $job1 = new Job;
- $job1->set('Description', 'job 1')
- ->save();
- $job2 = new Job;
- $job2->set('Description', 'job 2')
- ->setStatus('b')
- ->save();
-
- // save a change with fixStatus set to 'same'
- $change = new Change;
- $change->setDescription("change test 1.\n")
- ->addFile($file)
- ->setJobs(array($job1->getId(), $job2->getId()))
- ->setFixStatus('same')
- ->save();
-
- // verify job statuses before submit
- $this->assertSame('a', $job1->getStatus());
- $this->assertSame('b', $job2->getStatus());
-
- // submit a change and verify job statuses after
- $change->submit();
-
- $this->assertSame('a', Job::fetch($job1->getId())->getStatus());
- $this->assertSame('b', Job::fetch($job2->getId())->getStatus());
-
- // try again with a new change and fixStatus set to 'd'
- $file = new File;
- $file->setFilespec("//depot/bar")->setLocalContents("this is a test")->add();
- $change = new Change;
- $change->setDescription("change test 2.\n")
- ->addFile($file)
- ->setJobs(array($job1->getId(), $job2->getId()))
- ->setFixStatus('d')
- ->save()
- ->submit();
-
- // verify job statuses after submit
- $this->assertSame('d', Job::fetch($job1->getId())->getStatus());
- $this->assertSame('d', Job::fetch($job2->getId())->getStatus());
-
- // try again without using fixStatus
- $file = new File;
- $file->setFilespec("//depot/baz")->setLocalContents("this is a test")->add();
- $change = new Change;
- $change->setDescription("change test 3.\n")
- ->addFile($file)
- ->setJobs(array($job1->getId(), $job2->getId()))
- ->save()
- ->submit();
-
- // verify job statuses after submit
- $this->assertSame('closed', Job::fetch($job1->getId())->getStatus());
- $this->assertSame('closed', Job::fetch($job2->getId())->getStatus());
- }
-
- /**
- * Test getFiles and setFiles.
- */
- public function testGetSetFiles()
- {
- // test initial state of files in a fresh change object.
- $change = new Change;
- $files = $change->getFiles();
- $this->assertTrue(is_array($files), 'Expect an array from getFiles.');
- $this->assertSame(0, count($files), 'There should be no files associated with a fresh change.');
-
- // create two submitted changes, and one pending
- $file1 = new File;
- $filespec1 = '//depot/change1.txt';
- $file1->setFilespec($filespec1)->add()->setLocalContents('content1')->submit('File 1');
-
- $file2 = new File;
- $filespec2 = '//depot/change2.txt';
- $file2->setFilespec($filespec2)->add()->setLocalContents('content2')->submit('File 2');
-
- $file3 = new File;
- $filespec3 = '//depot/change3.txt';
- $file3->setFilespec($filespec3)->add()->setLocalContents('content3');
- $this->assertTrue($file3->isOpened(), 'File #3 should be opened.');
-
- // test that we get the appropriate files back for each change
- $change = Change::fetch(1);
- $this->assertFalse($change->isPending(), 'Change 1 should not be pending.');
- $files = $change->getFiles();
- $this->assertSame(1, count($files), 'There should be one file associated with change 1.');
- $this->assertSame($filespec1.'#1', $files[0], 'Expected filespec for change 1.');
-
- $change = Change::fetch(2);
- $this->assertFalse($change->isPending(), 'Change 2 should not be pending.');
- $files = $change->getFiles();
- $this->assertSame(1, count($files), 'There should be one file associated with change 2.');
- $this->assertSame($filespec2.'#1', $files[0], 'Expected filespec for change 2.');
-
- $change = Change::fetch('default');
- $this->assertTrue($change->isPending(), 'Change 3 should be pending.');
- $files = $change->getFiles();
- $this->assertSame(1, count($files), 'There should be one file associated with change 3.');
- $this->assertSame($filespec3, $files[0], 'Expected filespec for change 3.');
-
- // test that setting a comment on a pending changelist does not influence
- // getFiles handling.
- $change->setDescription('This is the default change.');
- $files = $change->getFiles();
- $this->assertSame(1, count($files), 'There should be one file associated with change 3.');
- $this->assertSame($filespec3, $files[0], 'Expected filespec for change 3.');
-
- // test that we cannot setFiles on a submitted changelist.
- try {
- $change = Change::fetch(2);
- $change->setFiles(array($filespec3));
- $this->fail('Unexpected success setting files on a submitted changelist.');
- } catch (SpecException $e) {
- $this->assertSame(
- 'Cannot set files on a submitted change.',
- $e->getMessage(),
- 'Expected error setting files on a submitted changelist'
- );
- } catch (\Exception $e) {
- $this->fail(
- 'Unexpected exception setting files on a submitted changelist: ('
- . get_class($e) .') '. $e->getMessage()
- );
- }
-
- // test that we can setFiles on a pending changelist, and that getFiles
- // returns the same list.
- $change = new Change;
- $change->setId('default');
- $change->setFiles(array($filespec1));
- $files = $change->getFiles();
- $this->assertSame(1, count($files), 'There should be one file associated with change 3 after setFiles.');
- $this->assertSame($filespec1, $files[0], 'Expected filespec for change 3 after setFiles.');
-
- // test that we can set the files to null to empty the list, and that we get
- // empty list in return.
- $change->setFiles(null);
- $files = $change->getFiles();
- $this->assertSame(0, count($files), 'There should now be no files associated with change 3.');
-
- // test that fetching a new change object returns the original files.
- $change = Change::fetch('default');
- $files = $change->getFiles();
- $this->assertSame(1, count($files), 'There should be one file associated with change 3.');
- $this->assertSame($filespec3, $files[0], 'Expected filespec for change 3.');
-
- // test that we can setFiles with an iterator of P4\File objects.
- $files = new FieldedIterator;
- $files[] = $file1;
- $files[] = $file2;
- $files[] = $file3;
- $change->setFiles($files);
- $files = $change->getFiles();
- $this->assertSame(3, count($files), 'There should now be three files associated with change 3.');
- $this->assertSame($filespec1, $files[0], 'Expected filespec for change 3, file 0.');
- $this->assertSame($filespec2, $files[1], 'Expected filespec for change 3, file 1.');
- $this->assertSame($filespec3, $files[2], 'Expected filespec for change 3, file 2.');
- }
-
- /**
- * Test accessors.
- */
- public function testAccessors()
- {
- // save a basic change.
- $change = new Change;
- $description = "this is a test\n";
- $change->setDescription($description);
- $change->save();
-
- // open a file for add.
- $file = new File;
- $file->setFilespec('//depot/test-file')
- ->add();
-
- // create a job.
- $job = new Job;
- $job->set('Description', 'fix something')
- ->save();
-
- // save a change with a file and a job.
- $change2 = new Change;
- $description2 = "a change with a file and a job.\n";
- $change2->setDescription($description2)
- ->setFiles(array($file->getFilespec()))
- ->setJobs(array($job->getId()))
- ->save();
-
- // ensure fetched change contains expected data.
- $tests = array(
-
- // test accessors for a brand new (unpopulated) change object.
- array(
- 'label' => __LINE__ . ': New change object',
- 'change' => new Change,
- 'method' => 'getId',
- 'expected' => null
- ),
- array(
- 'label' => __LINE__ . ': New change object',
- 'change' => new Change,
- 'method' => 'getDate',
- 'expected' => null
- ),
- array(
- 'label' => __LINE__ . ': New change object',
- 'change' => new Change,
- 'method' => 'getUser',
- 'expected' => $this->p4->getUser()
- ),
- array(
- 'label' => __LINE__ . ': New change object',
- 'change' => new Change,
- 'method' => 'getClient',
- 'expected' => $this->p4->getClient()
- ),
- array(
- 'label' => __LINE__ . ': New change object',
- 'change' => new Change,
- 'method' => 'getStatus',
- 'expected' => 'pending'
- ),
- array(
- 'label' => __LINE__ . ': New change object',
- 'change' => new Change,
- 'method' => 'getDescription',
- 'expected' => null
- ),
- array(
- 'label' => __LINE__ . ': New change object',
- 'change' => new Change,
- 'method' => 'getJobStatus',
- 'expected' => null
- ),
- array(
- 'label' => __LINE__ . ': New change object',
- 'change' => new Change,
- 'method' => 'getJobs',
- 'expected' => array()
- ),
-
- // test accessors for a saved change object.
- array(
- 'label' => __LINE__ . ': Saved change object',
- 'change' => $change,
- 'method' => 'getId',
- 'expected' => 1
- ),
- // datetime not tested here - see later test
- array(
- 'label' => __LINE__ . ': Saved change object',
- 'change' => $change,
- 'method' => 'getUser',
- 'expected' => $this->p4->getUser()
- ),
- array(
- 'label' => __LINE__ . ': Saved change object',
- 'change' => $change,
- 'method' => 'getClient',
- 'expected' => $this->p4->getClient()
- ),
- array(
- 'label' => __LINE__ . ': Saved change object',
- 'change' => $change,
- 'method' => 'getStatus',
- 'expected' => 'pending'
- ),
- array(
- 'label' => __LINE__ . ': Saved change object',
- 'change' => $change,
- 'method' => 'getDescription',
- 'expected' => $description
- ),
- array(
- 'label' => __LINE__ . ': Saved change object',
- 'change' => $change,
- 'method' => 'getJobStatus',
- 'expected' => null
- ),
- array(
- 'label' => __LINE__ . ': Saved change object',
- 'change' => $change,
- 'method' => 'getJobs',
- 'expected' => array()
- ),
-
- // test accessors for a change object with a file and a job attached.
- array(
- 'label' => __LINE__ . ': Change w. a file and a job',
- 'change' => $change2,
- 'method' => 'getId',
- 'expected' => 2
- ),
- // datetime not tested here - see later test
- array(
- 'label' => __LINE__ . ': Change w. a file and a job',
- 'change' => $change2,
- 'method' => 'getUser',
- 'expected' => $this->p4->getUser()
- ),
- array(
- 'label' => __LINE__ . ': Change w. a file and a job',
- 'change' => $change2,
- 'method' => 'getClient',
- 'expected' => $this->p4->getClient()
- ),
- array(
- 'label' => __LINE__ . ': Change w. a file and a job',
- 'change' => $change2,
- 'method' => 'getStatus',
- 'expected' => 'pending'
- ),
- array(
- 'label' => __LINE__ . ': Change w. a file and a job',
- 'change' => $change2,
- 'method' => 'getDescription',
- 'expected' => $description2
- ),
- array(
- 'label' => __LINE__ . ': Change w. a file and a job',
- 'change' => $change2,
- 'method' => 'getJobStatus',
- 'expected' => null
- ),
- array(
- 'label' => __LINE__ . ': Change w. a file and a job',
- 'change' => $change2,
- 'method' => 'getJobs',
- 'expected' => array($job->getId())
- ),
- );
-
- // run each test.
- foreach ($tests as $test) {
- $label = $test['label'] .' - '. $test['method'];
- $this->assertSame(
- $test['expected'],
- $test['change']->{$test['method']}(),
- "$label - expected value."
- );
- }
- }
-
- /**
- * test setDescription.
- */
- public function testSetDescription()
- {
- $tests = array(
- array(
- 'label' => __LINE__ .': null',
- 'description' => null,
- 'error' => false,
- ),
- array(
- 'label' => __LINE__ .': empty string',
- 'description' => '',
- 'error' => false,
- ),
- array(
- 'label' => __LINE__ .': array',
- 'description' => array(),
- 'error' => true,
- ),
- array(
- 'label' => __LINE__ .': integer',
- 'description' => 123,
- 'error' => true,
- ),
- array(
- 'label' => __LINE__ .': float',
- 'description' => -1.23,
- 'error' => true,
- ),
- array(
- 'label' => __LINE__ .': string',
- 'description' => "have a nice day\n",
- 'error' => false,
- ),
- );
-
- foreach ($tests as $test) {
- $change = new Change;
- $label = $test['label'];
- try {
- $change->setDescription($test['description']);
- if ($test['error']) {
- $this->fail("$label - Unexpected success.");
- }
- } catch (\InvalidArgumentException $e) {
- if ($test['error']) {
- $this->assertSame(
- 'Cannot set description. Invalid type given.',
- $e->getMessage(),
- "$label - Expected error."
- );
- } else {
- $this->fail("$label - Unexpected argument exception: ". $e->getMessage());
- }
- } catch (\Exception $e) {
- $this->fail("$label - Unexpected exception: (". get_class($e) .') '. $e->getMessage());
- }
-
- if (!$test['error']) {
- $this->assertSame(
- $test['description'],
- $change->getDescription(),
- "$label - Expect to get same description as set."
- );
- }
- }
- }
-
- /**
- * test setFiles.
- *
- * @todo add a test for setting files on a submitted change when submits work.
- */
- public function testSetFiles()
- {
- // create an iterator with files
- $files = new FieldedIterator;
- $file = new File;
- $files[] = $file->setFilespec('//depot/file1.txt');
- $file = new File;
- $files[] = $file->setFilespec('//depot/file2.txt');
- $file = new File;
- $files[] = $file->setFilespec('//depot/file3.txt');
-
- $filesIteratorInvalid = new FieldedIterator;
- $filesIteratorInvalid[] = $file;
- $filesIteratorInvalid[] = new Client;
-
- $tests = array(
- array(
- 'label' => __LINE__ .': iterator',
- 'files' => $files,
- 'error' => false,
- 'expected' => array(
- $files[0]->getFilespec(),
- $files[1]->getFilespec(),
- $files[2]->getFilespec(),
- ),
- ),
- array(
- 'label' => __LINE__ .': invalid iterator',
- 'files' => $filesIteratorInvalid,
- 'error' => new \InvalidArgumentException('All files must be a string or P4\File'),
- 'expected' => false,
- ),
- array(
- 'label' => __LINE__ .': null',
- 'files' => null,
- 'error' => false,
- 'expected' => array(),
- ),
- array(
- 'label' => __LINE__ .': empty string',
- 'files' => '',
- 'error' => new \InvalidArgumentException('Cannot set files. Invalid type given.'),
- 'expected' => false,
- ),
- array(
- 'label' => __LINE__ .': integer',
- 'files' => 123,
- 'error' => new \InvalidArgumentException('Cannot set files. Invalid type given.'),
- 'expected' => false,
- ),
- array(
- 'label' => __LINE__ .': float',
- 'files' => -1.23,
- 'error' => new \InvalidArgumentException('Cannot set files. Invalid type given.'),
- 'expected' => false,
- ),
- array(
- 'label' => __LINE__ .': string',
- 'files' => "have a nice day\n",
- 'error' => new \InvalidArgumentException('Cannot set files. Invalid type given.'),
- 'expected' => false,
- ),
- array(
- 'label' => __LINE__ .': empty array',
- 'files' => array(),
- 'error' => false,
- 'expected' => array(),
- ),
- array(
- 'label' => __LINE__ .': array with numerics',
- 'files' => array(1, 2),
- 'error' => new \InvalidArgumentException('All files must be a string or P4\File'),
- 'expected' => array(),
- ),
- array(
- 'label' => __LINE__ .': array with strings and numerics',
- 'files' => array('one', 2),
- 'error' => new \InvalidArgumentException('All files must be a string or P4\File'),
- 'expected' => array(),
- ),
- array(
- 'label' => __LINE__ .': array with a string',
- 'files' => array('one'),
- 'error' => false,
- 'expected' => array('one'),
- ),
- array(
- 'label' => __LINE__ .': array with multiple strings',
- 'files' => array('one', 'two', 'three'),
- 'error' => false,
- 'expected' => array('one', 'two', 'three'),
- ),
- );
-
- foreach ($tests as $test) {
- $change = new Change;
- $label = $test['label'];
- try {
- $change->setFiles($test['files']);
- if ($test['error']) {
- $this->fail("$label - Unexpected success.");
- }
- } catch (\Exception $e) {
- if ($test['error']) {
- $this->assertSame(
- $test['error']->getMessage(),
- $e->getMessage(),
- "$label - Expected error."
- );
- $this->assertSame(
- get_class($test['error']),
- get_class($e),
- "$label - Expected error class."
- );
- } else {
- $this->fail("$label - Unexpected exception: (". get_class($e) .') '. $e->getMessage());
- }
- }
-
- if (!$test['error']) {
- $this->assertTrue(
- is_array($change->getFiles()),
- "$label - Change getFiles should return array."
- );
- $this->assertSame(
- count($test['expected']),
- count($change->getFiles()),
- "$label - Change should contain same number of files."
- );
- }
- }
- }
-
- /**
- * test setJobs.
- *
- * @todo add a test for setting jobs on a submitted change when submits work.
- */
- public function testSetJobs()
- {
- // create an iterator with jobs.
- $jobs = new FieldedIterator;
- $job = new Job;
- $jobs[] = $job->setId('job000001');
- $job = new Job;
- $jobs[] = $job->setId('job000002');
- $job = new Job;
- $jobs[] = $job->setId('job000003');
-
- $jobsIteratorInvalid = new FieldedIterator;
- $jobsIteratorInvalid[] = $job;
- $jobsIteratorInvalid[] = new Client;
-
- // define inputs to set jobs.
- $tests = array(
- array(
- 'label' => __LINE__ .': iterator',
- 'jobs' => $jobs,
- 'error' => false,
- 'expected' => array('job000001', 'job000002', 'job000003'),
- ),
- array(
- 'label' => __LINE__ .': iterator with invalid element',
- 'jobs' => $jobsIteratorInvalid,
- 'error' => new \InvalidArgumentException('Each job must be a string.'),
- 'expected' => false,
- ),
- array(
- 'label' => __LINE__ .': null',
- 'jobs' => null,
- 'error' => false,
- 'expected' => array(),
- ),
- array(
- 'label' => __LINE__ .': empty string',
- 'jobs' => '',
- 'error' => new \InvalidArgumentException('Cannot set jobs. Invalid type given.'),
- 'expected' => false,
- ),
- array(
- 'label' => __LINE__ .': integer',
- 'jobs' => 123,
- 'error' => new \InvalidArgumentException('Cannot set jobs. Invalid type given.'),
- 'expected' => false,
- ),
- array(
- 'label' => __LINE__ .': float',
- 'jobs' => -1.23,
- 'error' => new \InvalidArgumentException('Cannot set jobs. Invalid type given.'),
- 'expected' => false,
- ),
- array(
- 'label' => __LINE__ .': string',
- 'jobs' => "have a nice day\n",
- 'error' => new \InvalidArgumentException('Cannot set jobs. Invalid type given.'),
- 'expected' => false,
- ),
- array(
- 'label' => __LINE__ .': empty array',
- 'jobs' => array(),
- 'error' => false,
- 'expected' => array(),
- ),
- array(
- 'label' => __LINE__ .': array with numerics',
- 'jobs' => array(1, 2),
- 'error' => new \InvalidArgumentException('Each job must be a string.'),
- 'expected' => array(),
- ),
- array(
- 'label' => __LINE__ .': array with strings and numerics',
- 'jobs' => array('one', 2),
- 'error' => new \InvalidArgumentException('Each job must be a string.'),
- 'expected' => array(),
- ),
- array(
- 'label' => __LINE__ .': array with a string',
- 'jobs' => array('one'),
- 'error' => false,
- 'expected' => array('one'),
- ),
- array(
- 'label' => __LINE__ .': array with multiple strings',
- 'jobs' => array('one', 'two', 'three'),
- 'error' => false,
- 'expected' => array('one', 'two', 'three'),
- ),
- );
-
- foreach ($tests as $test) {
- $change = new Change;
- $label = $test['label'];
- try {
- $change->setJobs($test['jobs']);
- if ($test['error']) {
- $this->fail("$label - Unexpected success.");
- }
- } catch (\Exception $e) {
- if ($test['error']) {
- $this->assertSame(
- $test['error']->getMessage(),
- $e->getMessage(),
- "$label - Expected error."
- );
- $this->assertSame(
- get_class($test['error']),
- get_class($e),
- "$label - Expected error class."
- );
- } else {
- $this->fail("$label - Unexpected exception: (". get_class($e) .') '. $e->getMessage());
- }
- }
-
- if (!$test['error']) {
- $this->assertSame(
- $test['expected'],
- $change->getJobs(),
- "$label - Expected jobs."
- );
- }
- }
- }
-
- /**
- * Test moving files between changelists.
- */
- public function testReopen()
- {
- // open a file for add.
- $file = new File;
- $file->setFilespec("//depot/test-file")
- ->add();
-
- // put the file in a change.
- $change1 = new Change;
- $change1->setDescription("test 1")
- ->addFile("//depot/test-file")
- ->save();
- $this->assertTrue(
- count($change1->getFiles()) == 1,
- "Change should have one file."
- );
- $this->assertTrue(
- in_array("//depot/test-file", $change1->getFiles()),
- "test-file should be in change."
- );
-
- // try to put the same file in a different change.
- $change2 = new Change;
- $change2->setDescription("test 2")
- ->addFile("//depot/test-file")
- ->save();
- $this->assertTrue(
- count($change2->getFiles()) == 1,
- "Change should have one file."
- );
- $this->assertTrue(
- in_array("//depot/test-file", $change2->getFiles()),
- "test-file should be in change."
- );
-
- // try to put same file back in first (now numbered) change.
- $change1->addFile("//depot/test-file")->save();
- $this->assertTrue(
- count($change1->getFiles()) == 1,
- "Change should have one file."
- );
- $this->assertTrue(
- in_array("//depot/test-file", $change1->getFiles()),
- "test-file should be in change."
- );
-
- // attempting to put a un-opened file in a change should fail.
- $change3 = new Change;
- try {
- $change3->setDescription("test 3")->addFile("//depot/foo")->save();
- $this->fail("Save change with unopened file should throw exception.");
- } catch (UnopenedException $e) {
- $this->assertTrue(true);
- }
- }
-
- /**
- * Test deleting changes.
- */
- public function testDelete()
- {
- // delete an unidentified change.
- $change = new Change;
- try {
- $change->delete();
- $this->fail("Delete change without id should fail.");
- } catch (SpecException $e) {
- $this->assertTrue(true);
- }
-
- // delete the default change.
- $change = new Change;
- $change->setId('default');
- try {
- $change->delete();
- $this->fail("Delete default change should fail.");
- } catch (SpecException $e) {
- $this->assertTrue(true);
- }
-
- // delete a non-existant change.
- $change = new Change;
- $change->setId('123');
- try {
- $change->delete();
- $this->fail("Delete non-existent change should fail.");
- } catch (NotFoundException $e) {
- $this->assertTrue(true);
- }
-
- // delete a real pending change w. no files.
- $change = new Change;
- $change->setDescription("test")->save()->delete();
- $this->assertFalse(
- Change::exists($change->getId()),
- "Deleted change should no longer exist."
- );
-
- // delete a real pending change w. files.
- $file = new File;
- $file->setFilespec("//depot/test-file")->add();
- $change = new Change;
- $change->setDescription("test")->addFile("//depot/test-file")->save()->delete();
- $this->assertFalse(
- Change::exists($change->getId()),
- "Deleted change should no longer exist."
- );
-
- // delete a real pending change w. jobs attached.
- $job = new Job;
- $job->set("Description", "test-job")->save();
- $change = new Change;
- $change->setDescription("test")->addJob($job->getId())->save()->delete();
- $this->assertFalse(
- Change::exists($change->getId()),
- "Deleted change should no longer exist."
- );
-
- // create a change under another client workspace.
- $client = new Client;
- $client->setId("another-test-client")
- ->setRoot(DATA_PATH . "/clients/another-test-client")
- ->save();
- $p4 = Connection::factory(
- $this->p4->getPort(),
- $this->p4->getUser(),
- $client->getId(),
- $this->getP4Params('password')
- );
- $change = new Change($p4);
- $change->setDescription("test-change")->save();
- $id = $change->getId();
-
- // delete another client's change.
- $change = Change::fetch($id);
- try {
- $change->delete();
- $this->fail("Delete of another client change should fail.");
- } catch (SpecException $e) {
- $this->assertTrue(true);
- }
-
- // delete again, but with force option.
- $change->delete(true);
- $this->assertFalse(
- Change::exists($change->getId()),
- "Deleted change should no longer exist."
- );
-
- // test delete of a submitted change.
- $file = new File;
- $file->setFilespec("//depot/foo")
- ->setLocalContents("this is a test")
- ->add()
- ->submit("test");
-
- $change = Change::fetch($file->getStatus('headChange'));
- try {
- $change->delete();
- $this->fail("Delete of submitted change should fail.");
- } catch (SpecException $e) {
- $this->assertSame(
- "Cannot delete a submitted change without the force option.",
- $e->getMessage(),
- "Unexpected exception message."
- );
- }
-
- try {
- $change->delete(true);
- $this->fail("Delete of submitted change should fail.");
- } catch (SpecException $e) {
- $this->assertSame(
- "Cannot delete a submitted change that contains files.",
- $e->getMessage(),
- "Unexpected exception message."
- );
- }
-
- // obliterate the files in change.
- $this->p4->run("obliterate", array("-y", "//...@=" . $change->getId()));
- $change = Change::fetch($change->getId());
- $change->delete(true);
- $this->assertFalse(
- Change::exists($change->getId()),
- "Change should no longer exist."
- );
- }
-
- /**
- * Test submitting of changes.
- */
- public function testSubmit()
- {
- $file = new File;
- $file->setFilespec("//depot/foo")->setLocalContents("this is a test")->add();
-
- // ensure no changes to start.
- $changes = Change::fetchAll();
- $this->assertSame(
- 0,
- count($changes),
- "There should be no changes."
- );
-
- // do a submit.
- $change = new Change;
- $change->addFile($file)->submit("test submit");
-
- // ensure change was successful.
- $changes = Change::fetchAll();
- $this->assertSame(
- 1,
- count($changes),
- "There should be one change."
- );
- $this->assertSame(
- Change::SUBMITTED_CHANGE,
- $change->getStatus(),
- "Change should be submitted."
- );
-
- // test that saving a submitted change results in exception
- try {
- $change->save();
- $this->fail('Unexpected success saving a submitted change.');
- } catch (SpecException $e) {
- $this->assertSame(
- 'Cannot update a submitted change without the force option.',
- $e->getMessage(),
- 'Expected exception saving a submitted change.'
- );
- } catch (\Exception $e) {
- $this->fail(
- 'Unexpected exception saving a submitted change ('
- . get_class($e) .') '. $e->getMessage()
- );
- }
-
- // test that submitted a submitted change results in exception
- try {
- $change->submit();
- $this->fail('Unexpected success submitting a submitted change.');
- } catch (SpecException $e) {
- $this->assertSame(
- 'Can only submit pending changes.',
- $e->getMessage(),
- 'Expected exception submitting a submitted change.'
- );
- } catch (\Exception $e) {
- $this->fail(
- 'Unexpected exception submitting a submitted change ('
- . get_class($e) .') '. $e->getMessage()
- );
- }
-
- // test that setting jobs on a submitted change results in exception
- try {
- $change->setJobs(array());
- $this->fail('Unexpected success setting jobs on a submitted change.');
- } catch (SpecException $e) {
- $this->assertSame(
- 'Cannot set jobs on a submitted change.',
- $e->getMessage(),
- 'Expected exception setting jobs on a submitted change.'
- );
- } catch (\Exception $e) {
- $this->fail(
- 'Unexpected exception setting jobs on a submitted change ('
- . get_class($e) .') '. $e->getMessage()
- );
- }
- }
-
- /**
- * Verify submit works if change is renumbered (was broken at one point).
- */
- public function testSubmitRenumber()
- {
- $file = new File;
- $file->setFilespec("//depot/foo")->setLocalContents("this is a test")->add();
-
- // make the change.
- $change = new Change;
- $change->addFile($file)->save();
-
- // make another change to induce renumbering
- $change2 = new Change;
- $change2->setDescription('bump change number')->save();
-
- $change->submit("test submit");
-
- // ensure submit was successful.
- $changes = Change::fetchAll();
- $this->assertSame(
- 2,
- count($changes),
- "There should be two changes."
- );
- $this->assertSame(
- Change::SUBMITTED_CHANGE,
- $change->getStatus(),
- "Change should be submitted."
- );
- }
-
- /**
- * Test submit resolve behavior.
- */
- public function testSubmitConflicts()
- {
- // create a second client.
- $client = new Client;
- $client->setId("client-2")
- ->setRoot($this->getP4Params('clientRoot') . '/client-2')
- ->addView("//depot/...", "//client-2/...")
- ->save();
-
- // connect w. second client.
- $p4 = Connection::factory(
- $this->p4->getPort(),
- $this->p4->getUser(),
- $client->getId(),
- $this->getP4Params('password')
- );
-
- // create a situation where resolve is needed.
- // a. from the main test client add/submit 'foo', then edit it.
- // b. from another client sync/edit/submit 'foo'.
- $file1 = new File;
- $file1->setFilespec("//depot/foo")
- ->setLocalContents("contents-1")
- ->add()
- ->submit("change 1")
- ->edit();
- $file2 = new File($p4);
- $file2->setFilespec("//depot/foo")
- ->sync()
- ->edit()
- ->submit("change 2");
-
- // try to submit a change w. files needing resolve.
- $change = new Change;
- try {
- $change->addFile($file1)
- ->submit("main client submit");
- $this->fail("Unexpected success; submit should fail.");
- } catch (ConflictException $e) {
- $files = $change->getFilesToResolve();
- $this->assertEquals(
- 1,
- count($files),
- "Expected one file needing resolve for submit."
- );
- $this->assertSame(
- $file1->getFilespec(),
- $files[0]->getFilespec(),
- "Expected matching filespecs."
- );
- }
-
- // create a situation where revert is needed.
- // a. from the main test client add 'foo'.
- // b. from another client add/submit 'foo'.
- $file1 = new File;
- $file1->setFilespec("//depot/bar")
- ->setLocalContents("contents-1")
- ->add();
- $file2 = new File($p4);
- $file2->setFilespec("//depot/bar")
- ->setLocalContents("contents-1")
- ->add()
- ->submit("change 2")
- ->edit()
- ->submit("change 3");
-
- // try to submit a change w. files needing revert.
- $change = new Change;
- try {
- $change->addFile($file1)
- ->submit("main client submit");
- $this->fail("Unexpected success; submit should fail.");
- } catch (ConflictException $e) {
- $files = $change->getFilesToRevert();
- $this->assertEquals(
- 1,
- count($files),
- "Expected one file needing resolve for revert."
- );
- $this->assertSame(
- $file1->getFilespec(),
- $files[0]->getFilespec(),
- "Expected matching filespecs."
- );
- }
-
- // create another situation where revert is needed.
- // a. from the main test client add/submit 'foo', then edit it.
- // b. from another client sync/delete/submit 'foo'.
- $file1 = new File;
- $file1->setFilespec("//depot/baz")
- ->setLocalContents("contents-1")
- ->add()
- ->submit("change 1")
- ->edit();
- $file2 = new File($p4);
- $file2->setFilespec("//depot/baz")
- ->sync()
- ->delete()
- ->submit("change 2");
-
- // try to submit a change w. files needing revert.
- $change = new Change;
- try {
- $change->addFile($file1)
- ->submit("main client submit");
- $this->fail("Unexpected success; submit should fail.");
- } catch (ConflictException $e) {
- $files = $change->getFilesToRevert();
- $this->assertSame(
- 1,
- count($files),
- "Expected one file needing resolve."
- );
- $this->assertSame(
- $file1->getFilespec(),
- $files[0]->getFilespec(),
- "Expected matching filespecs."
- );
- }
- }
-
- /**
- * Test submit resolve behavior when passing resolve options.
- */
- public function testSubmitResolveConflicts()
- {
- // create a second client.
- $client = new Client;
- $client->setId("client-2")
- ->setRoot($this->getP4Params('clientRoot') . '/client-2')
- ->addView("//depot/...", "//client-2/...")
- ->save();
-
- // connect w. second client.
- $p4 = Connection::factory(
- $this->p4->getPort(),
- $this->p4->getUser(),
- $client->getId(),
- $this->getP4Params('password')
- );
-
- // create a situation where resolve is needed.
- // a. from the main test client add/submit 'foo', then edit it.
- // b. from another client sync/edit/submit 'foo'.
- $file1 = new File;
- $file1->setFilespec("//depot/foo")
- ->setLocalContents("contents-1")
- ->add()
- ->submit("change 1")
- ->edit();
- $file2 = new File($p4);
- $file2->setFilespec("//depot/foo")
- ->sync()
- ->edit()
- ->submit("change 2");
-
- // try to submit a change w. files needing resolve.
- $change = new Change;
- $change->addFile($file1)
- ->submit("main client submit", Change::RESOLVE_ACCEPT_YOURS);
- }
-
- /**
- * test bad change numbers against P4\Validate\ChangeNumber
- */
- public function testBadValidateChangeNumber()
- {
- $tests = array (
- array(
- 'label' => __LINE__ .': null',
- 'value' => null,
- ),
- array(
- 'label' => __LINE__ .': empty',
- 'value' => '',
- ),
- array(
- 'label' => __LINE__ .': negative',
- 'value' => -1,
- ),
- array(
- 'label' => __LINE__ .': float',
- 'value' => 10.10,
- ),
- );
-
- foreach ($tests as $test) {
- $label = $test['label'];
-
- $validator = new \P4\Validate\ChangeNumber();
-
- $this->assertSame(
- false,
- $validator->isValid($test['value']),
- "$label - Expected Invalid"
- );
- }
- }
-
- /**
- * Test a fetch/save use case that failed in the past.
- */
- public function testSave()
- {
- $change = Change::fetch('default');
- $change->setDescription('Test submit')
- ->save();
- }
-
- /**
- * Test a save but with no description
- */
- public function testSaveNoDescription()
- {
- $change = Change::fetch('default');
- $change->save();
- }
-
- /**
- * Test updating a submitted change description.
- */
- public function testUpdateSubmitted()
- {
- $file = new File;
- $file->setFilespec("//depot/foo")
- ->setLocalContents("this is a test")
- ->add()
- ->submit('original description');
-
- $change = Change::fetch(1);
- $this->assertSame("original description\n", $change->getDescription());
-
- $change->setDescription('updated description')
- ->save(true);
-
- $change = Change::fetch(1);
- $this->assertSame("updated description\n", $change->getDescription());
- }
-
- /**
- * Test reverting an entire change.
- */
- public function testRevert()
- {
- $file1 = new File;
- $file1->setFilespec('//depot/one');
- $file1->add();
-
- $file2 = new File;
- $file2->setFilespec('//depot/two');
- $file2->add();
-
- $file3 = new File;
- $file3->setFilespec('//depot/three');
- $file3->add();
-
- $change = new Change;
- $change->setDescription("Test change");
- $change->setFiles(array('//depot/two', '//depot/three'));
- $change->save();
-
- // check that we have three files open.
- $query = FileQuery::create()
- ->addFilespec('//depot/...')
- ->setLimitToOpened(true);
- $this->assertSame(
- 3,
- File::fetchAll($query)->count(),
- "Expected three open files"
- );
-
- // revert the pending change.
- $change->revert();
-
- // check that we have one file open.
- $this->assertSame(
- 1,
- File::fetchAll($query)->count(),
- "Expected one open file"
- );
-
- // check that the correct files are opened.
- $file1->clearStatusCache();
- $this->assertTrue($file1->isOpened());
- $file2->clearStatusCache();
- $this->assertFalse($file2->isOpened());
- $file3->clearStatusCache();
- $this->assertFalse($file3->isOpened());
- }
-
- /**
- * Test getFileObjects and getFileObject
- */
- public function testGetFileObjectsObject()
- {
- $file1 = new File;
- $file1->setFilespec('//depot/one')
- ->add()
- ->setLocalContents("contents-1");
-
- $file2 = new File;
- $file2->setFilespec('//depot/two')
- ->add()
- ->setLocalContents("contents-2");
-
- $change = new Change;
- $change->setDescription("Test change")
- ->setFiles(array($file1, $file2))
- ->save();
-
- $this->assertSame(
- $change->getFiles(),
- $change->getFileObjects()->invoke('getDepotFilename'),
- 'Expected get files to match fileobject list pre-submit'
- );
-
- $change->submit('test');
- File::fetch('//depot/one')->edit()->submit('rev two');
-
- $this->assertSame(
- array('//depot/one#1', '//depot/two#1'),
- $change->getFiles(),
- 'Expected matching list of post-submit files'
- );
-
- $this->assertSame(
- array('1', '1'),
- $change->getFileObjects()->invoke('getStatus', array('headRev')),
- 'Expected properly reved file objects post submit'
- );
-
- $this->assertSame(
- '//depot/one',
- $change->getFileObject(File::fetch('//depot/one#2'))->getDepotFilename(),
- 'Expected getFileObject to provide proper depot filename'
- );
- $this->assertSame(
- '//depot/two',
- $change->getFileObject(File::fetch('//depot/two'))->getDepotFilename(),
- 'Expected getFileObject to provide proper depot filename'
- );
- $this->assertSame(
- '1',
- $change->getFileObject(File::fetch('//depot/one#2'))->getStatus('headRev'),
- 'Expected getFileObject to provide proper rev'
- );
-
- try {
- $change->getFileObject('//depot/three');
- $this->fail('Expected exception on getFileObject for invalid entry');
- } catch (\InvalidArgumentException $e) {
- }
-
- try {
- $change->getFileObject(12);
- $this->fail('Expected exception on getFileObject for invalid type');
- } catch (\InvalidArgumentException $e) {
-
- }
- }
-
- /**
- * Test getJobObjects and getJobObject
- */
- public function testGetJobObjectsObject()
- {
- $job1 = new Job;
- $job1->setId('job1')
- ->setDescription('job1')
- ->save();
- $job2 = new Job;
- $job2->setId('job2')
- ->setDescription('job2')
- ->save();
-
- $change = new Change;
- $change->setDescription("Test change")
- ->setJobs(array($job1, $job2))
- ->save();
-
- $this->assertSame(
- $change->getJobs(),
- $change->getJobObjects()->invoke('getId'),
- 'Expected get jobs to match job object list pre-submit'
- );
-
- $this->assertSame(
- 'job1',
- $change->getJobObject('job1')->getId(),
- 'Expected get job object to return result'
- );
-
- // verify invalid id fails
- try {
- $change->getJobObject('job3');
- } catch (\InvalidArgumentException $e) {
- }
- }
-
- /**
- * Test running fetch all with an array of ids
- */
- public function testFetchAllByIds()
- {
- $file = new File;
- $file->setFilespec("//depot/file")
- ->add()
- ->setLocalContents("one")
- ->submit("test");
- $file->edit()
- ->setLocalContents("two")
- ->submit("test2");
- $file->edit()
- ->setLocalContents("three")
- ->submit("test3");
-
- $changes = Change::fetchAll(array(Change::FETCH_BY_IDS => array('1', '3')));
-
- $this->assertSame(
- array(1, 3),
- $changes->invoke('getId'),
- 'expected matching result'
- );
- }
-
- /**
- * Test getting changes data
- */
- public function testGetChangesData()
- {
- $change = new Change;
- try {
- $change->getChangesData();
- $this->assertFalse(true, "Expected exception");
- } catch (\P4\Spec\Exception\Exception $e) {
- $this->assertTrue(true);
- }
-
- $file = new File;
- $file->setFilespec("//depot/file")
- ->add()
- ->setLocalContents("one")
- ->submit("test");
-
- $change = Change::fetch(1);
- $data = $change->getChangesData();
-
- $this->assertSame('1', $data['change']);
- $this->assertSame('tester', $data['user']);
- $this->assertSame('//depot/*', $data['path']);
-
- // ensure it doesn't take any more commands to get the change path
- // and original id -- we verify this by peeking at the log
- $original = Logger::hasLogger() ? Logger::getLogger() : null;
- $logger = new ZendLogger;
- $mock = new MockLog;
- $logger->addWriter($mock);
- Logger::setLogger($logger);
-
- $this->assertSame('//depot', $change->getPath());
- $this->assertSame(1, $change->getOriginalId());
- $this->assertSame(0, count($mock->events));
-
- // restore original logger if there is one.
- Logger::setLogger($original);
- }
-
- /**
- * Test setting a new client on an existing change
- */
- public function testSetClient()
- {
- $change = new Change($this->p4);
- $change->setDescription('test')->save();
- $oldClient = $change->getClient();
-
- $this->p4->getService('clients')->grab();
- $change->setClient($this->p4->getClient());
- $change->save();
- $this->p4->getService('clients')->release();
-
- $this->assertFalse(
- $oldClient == Change::fetch($change->getId(), $this->p4)->getClient(),
- 'expected client change to have saved.'
- );
- }
-
- /**
- * Test what happens when 'type' field isn't present
- */
- public function testNoType()
- {
- // journal patch the server so it appears to have a change spec that was locked at 2006.1
- $this->p4->run(
- 'admin',
- 'import',
- "@pv@ 0 @db.bodtext@ @change@ 0 @Change;code:201;fmt:L;rq;ro;seq:1;len:10;;Date;code:202;"
- . "type:date;opt:always;fmt:R;ro;seq:3;len:20;;Client;code:203;opt:always;fmt:L;ro;seq:2;"
- . "len:32;;User;code:204;opt:always;fmt:L;ro;seq:4;len:32;;Status;code:205;opt:always;"
- . "fmt:R;ro;seq:5;len:10;;Description;code:206;type:text;opt:required;rq;seq:6;;JobStatus;"
- . "code:207;type:select;fmt:I;seq:8;;Jobs;code:208;type:wlist;seq:7;len:32;;Files;code:210;"
- . "type:llist;len:64;;@\n"
- );
-
- $change = new Change;
- $this->assertSame(Change::PUBLIC_CHANGE, $change->getType());
- }
-
- /**
- * Test a saving with a different user id set
- */
- public function testSaveOtherUser()
- {
- // verify we cannot swap to an invalid user
- $this->assertFalse(User::exists('swapped'));
- $change = Change::fetch('default');
-
- try {
- $change->setDescription('Test submit')
- ->setUser('swapped')
- ->save();
- $this->fail('should not have worked');
- } catch (CommandException $e) {
- $this->assertSame(
- "Command failed: Error in change specification.\nUser swapped doesn't exist.",
- $e->getMessage()
- );
- }
-
-
- // add the user swapped to make later attempts work
- $user = new User;
- $user->setId('swapped')->setEmail('foo@bar.com')->setFullName('Swapped User')->save();
-
-
- // verify set user works on existing change
- $change = Change::fetch('default');
- $change->setDescription('Test submit2')
- ->save();
-
- $this->assertSame('tester', Change::fetch(1)->getUser());
-
- $change = Change::fetch(1);
- $change->setUser('swapped')->save();
-
- $this->assertSame('swapped', Change::fetch(1)->getUser());
-
-
- // verify out of the gate set user works
- $change = Change::fetch('default');
- $change->setDescription('Test submit')
- ->setUser('swapped')
- ->save();
-
- $this->assertSame('swapped', Change::fetch(2)->getUser());
- }
-
- /**
- * Test canAccess() method.
- */
- public function testCanAccess()
- {
- // create user 'foo' with limited access to depot
- $p4Foo = $this->connectWithAccess('foo', array('//depot/foo/...'));
-
- // create few changes to test with
- $file = new File;
- $file->setFilespec('//depot/test1')->open()->setLocalContents('abc');
- $change = new Change($this->p4);
- $change->setType(Change::RESTRICTED_CHANGE)->addFile($file)->submit('restricted #1'); // NOT accessible by 'foo'
- $id1 = $change->getId();
-
- $file = new File;
- $file->setFilespec('//depot/foo/test3')->open()->setLocalContents('def');
- $change = new Change($this->p4);
- $change->setType(Change::RESTRICTED_CHANGE)->addFile($file)->submit('restricted #2'); // accessible by 'foo'
- $id2 = $change->getId();
-
- $file = new File;
- $file->setFilespec('//depot/test2')->open()->setLocalContents('ghi');
- $change = new Change($this->p4);
- $change->setType(Change::PUBLIC_CHANGE)->addFile($file)->submit('public'); // accessible by 'foo'
- $id3 = $change->getId();
-
- // for users other than 'foo', all changes should be accessible
- $this->assertTrue(Change::fetch($id1, $this->p4)->canAccess());
- $this->assertTrue(Change::fetch($id2, $this->p4)->canAccess());
- $this->assertTrue(Change::fetch($id3, $this->p4)->canAccess());
-
- // ensure user 'foo' cannot access change id1, but can access the others
- $this->assertFalse(Change::fetch($id1, $p4Foo)->canAccess());
- $this->assertTrue(Change::fetch($id2, $p4Foo)->canAccess());
- $this->assertTrue(Change::fetch($id3, $p4Foo)->canAccess());
- }
-
- public function testGetFileData()
- {
- $files = array();
- for ($i = 0; $i < 10; $i++) {
- $file = new File;
- $files[] = $file;
- $file->setFilespec('//depot/' . $i)
- ->add()
- ->setLocalContents('contents-' . $i);
- }
-
- $change = new Change;
- $change->setDescription("Test change")
- ->setFiles($files)
- ->save('test');
-
- $change = Change::fetch(1);
- $files = $change->getFileData();
- $this->assertSame(10, count($files));
- $this->assertSame('//depot/0', current(current($files)));
-
- // test max limiting
- $files = $change->getFileData(null, 5);
- $this->assertSame(5, count($files));
-
- // test that shelved/unshelved files get cached separately
- $files = $change->getFileData(true);
- $this->assertSame(0, count($files));
- }
- }