- <?php
- /**
- * Test methods for the P4 api interface.
- *
- * @copyright 2012 Perforce Software. All rights reserved.
- * @license Please see LICENSE.txt in top-level folder of this distribution.
- * @version <release>/<patch>
- */
-
- namespace P4Test\Connection;
-
- use P4Test\TestCase;
- use P4\Connection\Connection;
- use P4\Connection\Exception\LoginException;
- use P4\Spec\User;
-
- abstract class InterfaceTest extends TestCase
- {
- /**
- * Test that connecting / disconnecting works.
- */
- public function testConnectAndDisconnect()
- {
- $this->p4->connect();
- $this->assertTrue($this->p4->isConnected());
- $this->p4->disconnect();
- $this->assertFalse($this->p4->isConnected());
-
- // try connecting to a bogus server.
- // should throw an exception.
- $this->p4->setPort('laskdjfskdlafj:4231');
- try {
- $this->p4->connect();
- $this->fail('Expect failure with a bogus server.');
- } catch (\P4\Connection\Exception\ConnectException $e) {
- $this->assertTrue(true);
- } catch (\Exception $e) {
- $this->fail('Unexpected exception: '. $e->getMessage());
- }
- }
-
- /**
- * Test setting/getting port.
- */
- public function testPort()
- {
- $this->p4->setPort('hostname.something.com:5555');
- $this->assertSame('hostname.something.com:5555', $this->p4->getPort(), 'Expect just-set port');
-
- // test port via constructor.
- $type = get_class($this->p4);
- $p4 = Connection::factory('1234', null, null, null, null, $type);
- $this->assertSame($p4->getPort(), '1234', 'Expect factory-set port.');
-
- // getPort should always return a string
- $this->p4->setPort(3333);
- $this->assertTrue(is_string($this->p4->getPort()));
- $this->assertSame($this->p4->getPort(), '3333', 'Expect correct string.');
- $this->assertEquals($this->p4->getPort(), 3333, 'Expect correct numeric value.');
- $this->assertNotSame($this->p4->getPort(), 3333, 'Expect port to be non-numeric.');
- }
-
- /**
- * Test setting/getting user.
- */
- public function testUser()
- {
- $this->p4->setUser('john_doe');
- $this->assertSame($this->p4->getUser(), 'john_doe', 'Expected user.');
-
- // test setting user via constructor.
- $type = get_class($this->p4);
- $p4 = Connection::factory(null, 'jdoe', null, null, null, $type);
- $this->assertSame($p4->getUser(), 'jdoe', 'Expected factory user.');
-
- // test user name acceptance
- $tests = array(
- // valid cases
- array('name' => 'jdoe', 'valid' => true),
- array('name' => 'jdoe.', 'valid' => true),
- array('name' => 'jdoe..', 'valid' => true),
- array('name' => 'j_doe', 'valid' => true),
- array('name' => 'jdoe"', 'valid' => true),
- array('name' => "jdoe'", 'valid' => true),
- array('name' => 'jdoe&', 'valid' => true),
- array('name' => 'jdoe1', 'valid' => true),
- array('name' => 'jdoe%%', 'valid' => true),
- array('name' => 'j/doe', 'valid' => true),
-
- // invalid cases
- array(
- 'name' => '1234',
- 'valid' => false,
- 'error' => 'Username: Purely numeric values are not allowed.'
- ),
- array(
- 'name' => 'john doe',
- 'valid' => false,
- 'error' => 'Username: Whitespace is not permitted.'
- ),
- array(
- 'name' => 'jdoe*',
- 'valid' => false,
- 'error' => "Username: Wildcards ('*', '...') are not permitted."
- ),
- array(
- 'name' => 'jdoe...',
- 'valid' => false,
- 'error' => "Username: Wildcards ('*', '...') are not permitted."
- ),
- array(
- 'name' => 'jdoe#',
- 'valid' => false,
- 'error' => "Username: Revision characters ('#', '@') are not permitted."
- ),
- array(
- 'name' => 'jdoe@',
- 'valid' => false,
- 'error' => "Username: Revision characters ('#', '@') are not permitted."
- ),
- );
- foreach ($tests as $test) {
- try {
- $this->p4->setUser($test['name']);
- if (!$test['valid']) {
- $this->fail("Expected '". $test['name'] ."' to fail.");
- }
- } catch (\P4\Exception $e) {
- if ($test['valid']) {
- $this->fail("Expected '". $test['name'] ."' to succeed: ". $e->getMessage());
- } else {
- $this->assertEquals(
- $test['error'],
- $e->getMessage(),
- 'Expected error message'
- );
- }
- } catch (\Exception $e) {
- $this->fail('Unexpected exception: '. $e->getMessage());
- }
- }
- }
-
- /**
- * Test setting/getting client.
- */
- public function testClient()
- {
- $this->p4->setClient('test-client');
- $this->assertSame('test-client', $this->p4->getClient(), 'Expected client.');
- $this->assertNotSame('blah', $this->p4->getClient(), 'Unexpected client.');
-
- // test setting client via constructor.
- $type = get_class($this->p4);
- $p4 = Connection::factory(null, null, 'jdoes_client', null, null, $type);
- $this->assertSame($p4->getClient(), 'jdoes_client', 'Expected factory client.');
-
- // test user name acceptance
- $tests = array(
- // valid cases
- array('client' => 'johnsclient', 'valid' => true),
- array('client' => 'johnsClient', 'valid' => true),
- array('client' => 'johns_client', 'valid' => true),
- array('client' => 'johns_client.', 'valid' => true),
- array('client' => 'johns_client..', 'valid' => true),
- array('client' => 'johns_client"', 'valid' => true),
- array('client' => "johns_client'", 'valid' => true),
- array('client' => 'johns_client&', 'valid' => true),
- array('client' => 'johns_client1', 'valid' => true),
-
- // invalid cases
- array(
- 'client' => '1234',
- 'valid' => false,
- 'error' => 'Client name: Purely numeric values are not allowed.'
- ),
- array(
- 'client' => 'johns client',
- 'valid' => false,
- 'error' => 'Client name: Whitespace is not permitted.'
- ),
- array(
- 'client' => 'johns_client*',
- 'valid' => false,
- 'error' => "Client name: Wildcards ('*', '...') are not permitted."
- ),
- array(
- 'client' => 'johns_client...',
- 'valid' => false,
- 'error' => "Client name: Wildcards ('*', '...') are not permitted."
- ),
- array(
- 'client' => 'johns_client%%',
- 'valid' => false,
- 'error' => "Client name: Positional specifiers ('%%x') are not permitted."
- ),
- array(
- 'client' => 'johns_client#',
- 'valid' => false,
- 'error' => "Client name: Revision characters ('#', '@') are not permitted."
- ),
- array(
- 'client' => 'johns_client@',
- 'valid' => false,
- 'error' => "Client name: Revision characters ('#', '@') are not permitted."
- ),
- );
- foreach ($tests as $test) {
- try {
- $this->p4->setClient($test['client']);
- if (!$test['valid']) {
- $this->fail("Expected '". $test['client'] ."' to fail.");
- }
- } catch (\P4\Exception $e) {
- if ($test['valid']) {
- $this->fail("Expected '". $test['client'] ."' to succeed: ". $e->getMessage());
- } else {
- $this->assertEquals(
- $test['error'],
- $e->getMessage(),
- 'Expected error message'
- );
- }
- } catch (\Exception $e) {
- $this->fail('Unexpected exception: '. $e->getMessage());
- }
- }
- }
-
- /**
- * Test setting/getting password.
- */
- public function testPassword()
- {
- $this->p4->setPassword('test-password');
- $this->assertSame('test-password', $this->p4->getPassword());
- $this->assertNotSame('blah', $this->p4->getPassword());
-
- // test setting password via constructor.
- $type = get_class($this->p4);
- $p4 = Connection::factory(null, null, null, 'secret key', null, $type);
- $this->assertTrue($p4->getPassword() == 'secret key');
- }
-
-
- /**
- * Test setting/getting ticket.
- */
- public function testTicket()
- {
- $this->p4->setTicket('ALKSJROIEL2134235');
- $this->assertSame('ALKSJROIEL2134235', $this->p4->getTicket(), 'Expected ticket.');
- $this->assertNotSame('blah', $this->p4->getTicket(), 'Unexpected ticket.');
-
- // test setting ticket via constructor.
- $type = get_class($this->p4);
- $p4 = Connection::factory(null, null, null, null, 'ALKSJROIEL2134235', $type);
- $this->assertSame($p4->getTicket(), 'ALKSJROIEL2134235', 'Expected factory ticket.');
- }
-
- /**
- * Test Connection identity.
- */
- public function testConnectionIdentity()
- {
- $identity = $this->p4->getConnectionIdentity();
- $this->assertTrue(isset($identity['name']), 'Expect name is set.');
- $this->assertTrue(isset($identity['platform']), 'Expect platform is set.');
- $this->assertTrue(isset($identity['version']), 'Expect version is set.');
- $this->assertTrue(isset($identity['build']), 'Expect build is set.');
- $this->assertTrue(isset($identity['apiversion']), 'Expect apiversion is set.');
- $this->assertTrue(isset($identity['apibuild']), 'Expect apibuild is set.');
- $this->assertTrue(isset($identity['date']), 'Expect date is set.');
- $this->assertTrue(isset($identity['original']), 'Expect original is set.');
- }
-
- /**
- * Test get info.
- */
- public function testGetInfo()
- {
- $info = $this->p4->getInfo();
- $this->assertTrue(is_array($info), 'Expect info to be an array.');
- $this->assertTrue(isset($info['userName']), 'Expect userName is set.');
- $this->assertTrue(isset($info['clientName']), 'Expect clientName is set.');
- $this->assertTrue(isset($info['clientCwd']), 'Expect clientCwd is set.');
- $this->assertTrue(isset($info['clientHost']), 'Expect clientHost is set.');
- $this->assertTrue(isset($info['clientAddress']), 'Expect clientAddress is set.');
- $this->assertTrue(isset($info['serverAddress']), 'Expect serverAddress is set.');
- $this->assertTrue(isset($info['serverRoot']), 'Expect serverRoot is set.');
- $this->assertTrue(isset($info['serverDate']), 'Expect serverDate is set.');
- $this->assertTrue(isset($info['serverUptime']), 'Expect serverUptime is set.');
- $this->assertTrue(isset($info['serverVersion']), 'Expect serverVersion is set.');
- $this->assertTrue(isset($info['serverLicense']), 'Expect serverLicense is set.');
-
- // test that cache is cleared when connection params change.
- $this->p4->setClient('a-client');
- $info2 = $this->p4->getInfo();
- $this->assertNotEquals(serialize($info), serialize($info2), 'Unexpected client match.');
- }
-
- /**
- * Test client root accessor.
- */
- public function testGetClientRoot()
- {
- $root = $this->p4->getClientRoot();
- $info = $this->p4->getInfo();
- if ($root) {
- $this->assertSame($root, $info['clientRoot'], 'Expect root to match info.');
- } else {
- $this->assertFalse(isset($info['clientRoot']), 'Unexpected clientRoot with no root.');
- }
- }
-
- /**
- * Test login/authentication.
- */
- public function testLogin()
- {
- $this->assertTrue(
- $this->p4->isAuthenticated(),
- 'Expected user to be authenticated'
- );
-
- try {
- $ticket = $this->p4->login();
- $this->assertTrue(strlen($ticket) > 0, "Expected login ticket");
- } catch (LoginException $e) {
- $this->fail("Expected login to succeed");
- }
-
- try {
- $this->p4->setPassword('alskdfj23523');
- $ticket = $this->p4->login();
- $this->fail("Expected login failure");
- } catch (LoginException $e) {
- $this->assertSame(
- LoginException::CREDENTIAL_INVALID,
- $e->getCode(),
- "Expected credential invalid login exception"
- );
- }
-
- // erase the successful login ticket so that the password is re-evaluated
- $this->p4->setTicket(null);
- $this->assertFalse(
- $this->p4->isAuthenticated(),
- 'Expected user not to be authenticated'
- );
-
- try {
- $this->p4->setUser('laksdjflkasdfj');
- $ticket = $this->p4->login();
- $this->fail("Expected login failure");
- } catch (LoginException $e) {
- $this->assertSame(
- LoginException::IDENTITY_NOT_FOUND,
- $e->getCode(),
- "Expected identity not found login exception"
- );
- }
- }
-
- /**
- * Test running a command.
- */
- public function testRun()
- {
- $result = $this->p4->run('users', null, null, false);
- $this->assertFalse($result->isTagged(), 'Expect untagged result.');
-
- $result = $this->p4->run('users');
- $this->assertTrue($result->isTagged(), 'Expect tagger result.');
-
- $data = $result->getData();
- $this->assertSame(
- serialize($result->getData(0)),
- serialize($data[0]),
- 'Expect getData match.'
- );
- $this->assertSame(
- $result->getData(0, 'User'),
- $data[0]['User'],
- 'Expect getData User match.'
- );
- $this->assertSame($result->getCommand(), 'users', 'Expect command match.');
- }
-
- /**
- * Test super user detection.
- */
- public function testSuperUser()
- {
- // instance _p4 object runs as super.
- $this->assertTrue($this->p4->isSuperUser(), 'Connection should have super user privs.');
-
- // create un-privileged user.
- $user = $this->p4->run('user', array('-o', 'jdoe'));
- $this->p4->run('user', array('-i', '-f'), $user->getData(-1));
-
- // connect as un-privileged user.
- $class = get_class($this->p4);
- $p4 = new $class;
- $p4->setUser('jdoe');
- $p4->setPort($this->p4->getPort());
- $p4->connect();
- $this->assertFalse($p4->isSuperUser(), 'Connection should not have super user privs.');
- }
-
- /**
- * Test the security level.
- */
- public function testSecurityLevel()
- {
- // should be zero to start.
- $this->assertTrue($this->p4->getSecurityLevel() == 0, "Expected security level zero");
-
- $counter = new \P4\Counter\Counter;
- $counter->setId('security');
-
- // 1
- $counter->set(1, true);
- $this->assertTrue($this->p4->getSecurityLevel() == 1, "Expected security level one");
-
- // 2
- $counter->set(2, true);
-
- // once the security counter is increased to 2, the current user's password must be reset.
- // see the Perforce System Administrator's Guide, Chapter 3, Server Security Levels
- // http://www.perforce.com/perforce/doc.current/manuals/p4sag/03_superuser.html#1081537
- //
- // Unfortunately, this doesn't work the way you'd hope. The desire would be to write
- // something similar to:
- //
- // $user = \P4\Spec\User::fetch($this->p4->getUser())
- // ->setPassword('testing321')
- // ->save();
- //
- // Calling setPassword() invokes other Perforce commands prior to the password command
- // (for lazy loading, verifying protections, etc.), and these commands will fail due
- // to the password reset requirement.
- //
- // So, we need to directly execute the password command.
- $newPassword = 'newPassword123';
- $this->p4->run(
- 'password',
- null,
- array(
- $this->p4->getPassword(),
- $newPassword,
- $newPassword
- )
- );
- $this->p4->setPassword($newPassword);
-
- $this->assertTrue($this->p4->getSecurityLevel() == 2, "Expected security level two");
-
- // 3
- $counter->set(3, true);
-
- // must login.
- $this->p4->login();
-
- $this->assertTrue($this->p4->getSecurityLevel() == 3, "Expected security level three");
- }
-
- /**
- * Test app name
- */
- public function testAppName()
- {
- $root = $this->getP4Params('serverRoot');
- $port = $this->p4->getPort() . ' -vrpc=3 -L ' . $root . '/test-log';
- $p4 = Connection::factory(
- $port,
- $this->getP4Params('user'),
- $this->getP4Params('client'),
- $this->getP4Params('password')
- );
-
- // verify can set/get app name
- $p4->setAppName('some-name');
- $this->assertSame('some-name', $p4->getAppName());
-
- // verify can set/get program name
- $p4->setProgName('my-prog');
- $this->assertSame('my-prog', $p4->getProgName());
-
- // verify can set/get program version
- $p4->setProgVersion('2013.1.WHATEVER/123456');
- $this->assertSame('2013.1.WHATEVER/123456', $p4->getProgVersion());
-
- // verify server sees app name
- $p4->getInfo();
- $log = file_get_contents($root . '/test-log');
- $this->assertTrue(strpos($log, 'app = some-name') !== false, "Looking for app name in log.");
- }
-
- /**
- * Test the service locator facilities of the connection
- */
- public function testServiceLocator()
- {
- $p4 = $this->p4;
-
- // should throw for non-existent services
- try {
- $p4->getService('foo');
- $this->fail();
- } catch (\P4\Connection\Exception\ServiceNotFoundException $e) {
- $this->assertTrue(true);
- }
-
- // should require a object or callable
- try {
- $p4->setService('foo', 'bar');
- $this->fail();
- } catch (\InvalidArgumentException $e) {
- $this->assertTrue(true);
- }
-
- // configure a basic service
- $service = new \stdClass;
- $service->value = true;
- $p4->setService('foo', $service);
- $this->assertSame($service, $p4->getService('foo'));
-
- // configure a service factory
- $factory = function ($p4, $name) {
- $service = new \stdClass;
- $service->p4 = $p4;
- $service->name = $name;
- return $service;
- };
- $p4->setService('baz', $factory);
- $service = $p4->getService('baz');
- $this->assertTrue($service instanceof \stdClass);
- $this->assertSame($p4, $service->p4);
- $this->assertSame('baz', $service->name);
-
- // once created, service should be reused
- $this->assertTrue($service === $p4->getService('baz'));
- }
-
- public function testIsAuthenticated()
- {
- $user = new User($this->p4);
- $user->setId('jdoe');
- $user->setPassword('abc123');
- $user->setEmail('jdoe@example.com');
- $user->setFullName('Jonathan H. Doe');
- $user->save();
-
- $user2 = new User($this->p4);
- $user2->setId('jdoe2');
- $user2->setEmail('jdoe2@example.com');
- $user2->setFullName('Jonathan H. DEUX');
- $user2->save();
-
- // test valid password
- $p4 = Connection::factory($this->p4->getPort(), 'jdoe', null, 'abc123');
- $this->assertTrue($p4->isAuthenticated());
-
- // test invalid password
- $p4 = Connection::factory($this->p4->getPort(), 'jdoe', null, 'xyz789');
- $this->assertFalse($p4->isAuthenticated());
-
- // test valid ticket
- $p4 = Connection::factory($this->p4->getPort(), 'jdoe', null, 'abc123');
- $p4->login();
- $p4->setPassword(null);
- $this->assertNotNull($p4->getTicket());
- $p4->setTicket($p4->getTicket());
- $this->assertTrue($p4->isAuthenticated());
-
- // test invalid ticket
- $ticket = $p4->getTicket();
- $p4->run('logout');
- $p4->disconnect();
- $this->assertFalse($p4->isAuthenticated());
-
- // test edge case of valid blank password
- $p4 = Connection::factory($this->p4->getPort(), 'jdoe2', null, null);
- $this->assertTrue($p4->isAuthenticated());
-
- // test edge case of invalid password (when login not required)
- $p4 = Connection::factory($this->p4->getPort(), 'jdoe2', null, 'abc123');
- $this->assertFalse($p4->isAuthenticated());
-
- // test invalid ticket (recycling invalid ticket from above)
- $p4 = Connection::factory($this->p4->getPort(), 'jdoe2', null, null, $ticket);
- $this->assertFalse($p4->isAuthenticated());
- }
- }