openstack-sdk-php/tests/Tests/ObjectStore/v1/StreamWrapperTest.php
Matt Farina 153e6e8b67 Updated the directory and namespace structure to support
multiple API verstions for each service. This included
updating the directory structure for the tests and moving
the Common files to a Common namespace.

Implements blueprint multiple-api-versions

Change-Id: I9f9dfc4ef8f4172243519772a9af86dd92690fcf
2014-04-29 12:02:47 -04:00

618 lines
17 KiB
PHP

<?php
/* ============================================================================
(c) Copyright 2012-2014 Hewlett-Packard Development Company, L.P.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
============================================================================ */
/**
* Unit tests for the stream wrapper.
*/
namespace OpenStack\Tests\ObjectStore\v1\Resource;
use \OpenStack\ObjectStore\v1\Resource\StreamWrapper;
use \OpenStack\ObjectStore\v1\Resource\Container;
use \OpenStack\ObjectStore\v1\Resource\Object;
/**
* @group streamWrapper
*/
class StreamWrapperTest extends \OpenStack\Tests\TestCase
{
const FNAME = 'streamTest.txt';
const FTYPE = 'application/x-tuna-fish; charset=iso-8859-13';
/**
* Cleaning up the test container so we can reuse it for other tests.
*/
public static function tearDownAfterClass()
{
// First we get an identity
$user = self::conf('openstack.identity.username');
$pass = self::conf('openstack.identity.password');
$tenantId = self::conf('openstack.identity.tenantId');
$url = self::conf('openstack.identity.url');
$ident = new \OpenStack\Identity\v2\IdentityService($url, self::getTransportClient());
$token = $ident->authenticateAsUser($user, $pass, $tenantId);
// Then we need to get an instance of storage
$store = \OpenStack\ObjectStore\v1\ObjectStorage::newFromIdentity($ident, self::conf('openstack.swift.region'), self::getTransportClient());
// Delete the container and all the contents.
$cname = self::$settings['openstack.swift.container'];
try {
$container = $store->container($cname);
}
// The container was never created.
catch (\OpenStack\Common\Transport\Exception\FileNotFoundException $e) {
return;
}
foreach ($container as $object) {
try {
$container->delete($object->name());
} catch (\Exception $e) {}
}
$store->deleteContainer($cname);
}
protected function newUrl($objectName)
{
$scheme = StreamWrapper::DEFAULT_SCHEME;
$cname = self::$settings['openstack.swift.container'];
$cname = urlencode($cname);
$objectParts = explode('/', $objectName);
for ($i = 0; $i < count($objectParts); ++$i) {
$objectParts[$i] = urlencode($objectParts[$i]);
}
$objectName = implode('/', $objectParts);
$url = $scheme . '://' . $cname . '/' . $objectName;
return $url;
}
/**
* This assumes auth has already been done.
*/
protected function basicSwiftContext($add = array(), $scheme = null)
{
$cname = self::$settings['openstack.swift.container'];
if (empty($scheme)) {
$scheme = StreamWrapper::DEFAULT_SCHEME;
}
if (empty(self::$ostore)) {
throw new \Exception('OStore is gone.');
}
$params = $add + array(
'token' => $this->objectStore()->token(),
'swift_endpoint' => $this->objectStore()->url(),
'content_type' => self::FTYPE,
'transport_client' => $this->getTransportClient(),
);
$cxt = array($scheme => $params);
return stream_context_create($cxt);
}
/**
* This performs authentication via context.
*/
protected function authSwiftContext($add = array(), $scheme = null)
{
$cname = self::$settings['openstack.swift.container'];
$username = self::$settings['openstack.identity.username'];
$password = self::$settings['openstack.identity.password'];
$tenant = self::$settings['openstack.identity.tenantId'];
$baseURL = self::$settings['openstack.identity.url'];
if (empty($scheme)) {
$scheme = StreamWrapper::DEFAULT_SCHEME;
}
$params = $add + array(
'username' => $username,
'password' => $password,
'endpoint' => $baseURL,
'tenantid' => $tenant,
'content_type' => self::FTYPE,
'transport_client' => $this->getTransportClient(),
);
$cxt = array($scheme => $params);
return stream_context_create($cxt);
}
/**
* Add additional params to the config.
*
* This allows us to insert credentials into the
* bootstrap config, which in turn allows us to run
* high-level context-less functions like
* file_get_contents(), stat(), and is_file().
*/
protected function addBootstrapConfig()
{
$opts = array(
'username' => self::$settings['openstack.identity.username'],
'password' => self::$settings['openstack.identity.password'],
'endpoint' => self::$settings['openstack.identity.url'],
'tenantid' => self::$settings['openstack.identity.tenantId'],
'token' => $this->objectStore()->token(),
'swift_endpoint' => $this->objectStore()->url(),
);
\OpenStack\Bootstrap::setConfiguration($opts);
}
// Canary. There are UTF-8 encoding issues in stream wrappers.
public function testStreamContext()
{
// Reset this in case something else left its
// auth token lying around.
\OpenStack\Bootstrap::setConfiguration(array(
'token' => null,
));
$cxt = $this->authSwiftContext();
$array = stream_context_get_options($cxt);
$opts = $array['swift'];
$endpoint = self::conf('openstack.identity.url');
$this->assertEquals($endpoint, $opts['endpoint'], 'A UTF-8 encoding issue.');
}
/**
* @depends testStreamContext
*/
public function testRegister()
{
// Canary
$this->assertNotEmpty(StreamWrapper::DEFAULT_SCHEME);
$klass = '\OpenStack\ObjectStore\v1\Resource\StreamWrapper';
stream_wrapper_register(StreamWrapper::DEFAULT_SCHEME, $klass);
$wrappers = stream_get_wrappers();
$this->assertContains(StreamWrapper::DEFAULT_SCHEME, $wrappers);
}
/**
* @depends testRegister
*/
public function testOpenFailureWithoutContext()
{
$cname = self::$settings['openstack.swift.container'];
// Create a fresh container.
$this->eradicateContainer($cname);
$this->containerFixture();
$url = $this->newUrl('foo→/bar.txt');
$ret = @fopen($url, 'r');
$this->assertFalse($ret);
}
/**
* @depends testRegister
*/
public function testOpen()
{
$cname = self::$settings['openstack.swift.container'];
// Create a fresh container.
$this->eradicateContainer($cname);
$this->containerFixture();
// Simple write test.
$oUrl = $this->newUrl('foo→/test.csv');
$res = fopen($oUrl, 'nope', false, $this->authSwiftContext());
$this->assertTrue(is_resource($res));
$md = stream_get_meta_data($res);
$wrapper = $md['wrapper_data'];
fclose($res);
// Now we test the same, but re-using the auth token:
$cxt = $this->basicSwiftContext(array('token' => $wrapper->token()));
$res = fopen($oUrl, 'nope', false, $cxt);
$this->assertTrue(is_resource($res));
fclose($res);
}
/**
* @depends testOpen
*/
public function testOpenFailureWithRead()
{
$url = $this->newUrl(__FUNCTION__);
$res = @fopen($url, 'r', false, $this->basicSwiftContext());
$this->assertFalse($res);
}
// DO we need to test other modes?
/**
* @depends testOpen
*/
public function testOpenCreateMode()
{
$url = $this->newUrl(self::FNAME);
$res = fopen($url, 'c+', false, $this->basicSwiftContext());
$this->assertTrue(is_resource($res));
//fclose($res);
return $res;
}
/**
* @depends testOpenCreateMode
*/
public function testTell($res)
{
// Sould be at the beginning of the buffer.
$this->assertEquals(0, ftell($res));
return $res;
}
/**
* @depends testTell
*/
public function testWrite($res)
{
$str = 'To be is to be the value of a bound variable. -- Quine';
fwrite($res, $str);
$this->assertGreaterThan(0, ftell($res));
return $res;
}
/**
* @depends testWrite
*/
public function testStat($res)
{
$stat = fstat($res);
$this->assertGreaterThan(0, $stat['size']);
return $res;
}
/**
* @depends testStat
*/
public function testSeek($res)
{
$then = ftell($res);
rewind($res);
$now = ftell($res);
// $now should be 0
$this->assertLessThan($then, $now);
$this->assertEquals(0, $now);
fseek($res, 0, SEEK_END);
$final = ftell($res);
$this->assertEquals($then, $final);
return $res;
}
/**
* @depends testSeek
*/
public function testEof($res)
{
rewind($res);
$this->assertEquals(0, ftell($res));
$this->assertFalse(feof($res));
fseek($res, 0, SEEK_END);
$this->assertGreaterThan(0, ftell($res));
$read = fread($res, 8192);
$this->assertEmpty($read);
$this->assertTrue(feof($res));
return $res;
}
/**
* @depends testEof
*/
public function testFlush($res)
{
$stat1 = fstat($res);
fflush($res);
// Grab a copy of the object.
$url = $this->newUrl(self::FNAME);
$newObj = fopen($url, 'r', false, $this->basicSwiftContext());
$stat2 = fstat($newObj);
$this->assertEquals($stat1['size'], $stat2['size']);
return $res;
}
/**
* @depends testFlush
*/
public function testStreamGetMetadata($res)
{
// Grab a copy of the object.
$url = $this->newUrl(self::FNAME);
$newObj = fopen($url, 'r', false, $this->basicSwiftContext());
$md = stream_get_meta_data($newObj);
//throw new \Exception(print_r($md, true));
$obj = $md['wrapper_data']->object();
$this->assertInstanceOf('\OpenStack\ObjectStore\v1\Resource\RemoteObject', $obj);
$this->assertEquals(self::FTYPE, $obj->contentType());
}
/**
* @depends testFlush
*/
public function testClose($res)
{
$this->assertTrue(is_resource($res));
fwrite($res, '~~~~');
//throw new \Exception(stream_get_contents($res));
fflush($res);
// This is occasionally generating seemingly
// spurious PHP errors about Bootstrap::$config.
fclose($res);
$url = $this->newUrl(self::FNAME);
$res2 = fopen($url, 'r', false, $this->basicSwiftContext());
$this->assertTrue(is_resource($res2));
$contents = stream_get_contents($res2);
fclose($res2);
$this->assertRegExp('/~{4}$/', $contents);
}
/**
* @depends testClose
*/
public function testCast()
{
$url = $this->newUrl(self::FNAME);
$res = fopen($url, 'r', false, $this->basicSwiftContext());
$read = array($res);
$write = array();
$except = array();
$num_changed = stream_select($read, $write, $except, 0);
$this->assertGreaterThan(0, $num_changed);
}
/**
* @depends testClose
*/
public function testUrlStat()
{
// Add context to the bootstrap config.
$this->addBootstrapConfig();
$url = $this->newUrl(self::FNAME);
$ret = stat($url);
// Check that the array looks right.
$this->assertEquals(26, count($ret));
$this->assertEquals(0, $ret[3]);
$this->assertEquals($ret[2], $ret['mode']);
$this->assertTrue(file_exists($url));
$this->assertTrue(is_readable($url));
$this->assertTrue(is_writeable($url));
$this->assertFalse(is_link($url));
$this->assertGreaterThan(0, filemtime($url));
$this->assertGreaterThan(5, filesize($url));
$perm = fileperms($url);
// Assert that this is a file. Objects are
// *always* marked as files.
$this->assertEquals(0x8000, $perm & 0x8000);
// Assert writeable by owner.
$this->assertEquals(0x0080, $perm & 0x0080);
// Assert not world writable.
$this->assertEquals(0, $perm & 0x0002);
$contents = file_get_contents($url);
$this->assertGreaterThan(5, strlen($contents));
$fsCopy = '/tmp/hpcloud-copy-test.txt';
copy($url, $fsCopy, $this->basicSwiftContext());
$this->assertTrue(file_exists($fsCopy));
unlink($fsCopy);
}
/**
* @depends testFlush
*/
public function testUnlink()
{
$url = $this->newUrl(self::FNAME);
$cxt = $this->basicSwiftContext();
$ret = unlink($url, $cxt);
$this->assertTrue($ret);
$ret2 = unlink($url, $cxt);
$this->assertFalse($ret2);
}
public function testSetOption()
{
$url = $this->newUrl('fake.foo');
$fake = fopen($url, 'nope', false, $this->basicSwiftContext());
$this->assertTrue(stream_set_blocking($fake, 1));
// Returns 0 on success.
$this->assertEquals(0, stream_set_write_buffer($fake, 8192));
// Cant set a timeout on a tmp storage:
$this->assertFalse(stream_set_timeout($fake, 10));
fclose($fake);
}
/**
* @depends testUnlink
*/
public function testRename()
{
$url = $this->newUrl('rename.foo');
$fake = fopen($url, 'w+', false, $this->basicSwiftContext());
fwrite($fake, 'test');
fclose($fake);
$this->assertTrue(file_exists($url));
$url2 = $this->newUrl('rename.txt');
rename($url, $url2, $this->basicSwiftContext());
$this->assertTrue(file_exists($url2));
$this->assertFalse(file_exists($url));
unlink($url2, $this->basicSwiftContext());
}
/**
* @depends testUnlink
*/
public function testOpenDir()
{
$urls = array('test1.txt', 'foo/test2.txt', 'foo/test3.txt', 'bar/test4.txt');
foreach ($urls as $base) {
$url = $this->newUrl($base);
$f = fopen($url, 'c+', false, $this->basicSwiftContext());
fwrite($f, 'Test.');
fclose($f);
}
$dirUrl = $this->newUrl('');
$dir = opendir($dirUrl, $this->basicSwiftContext());
$this->assertTrue(is_resource($dir));
return $dir;
}
/**
* @depends testOpenDir
*/
public function testReaddir($dir)
{
// Order should be newest to oldest.
$expects = array('bar/', 'foo/', 'test1.txt');
$buffer = array();
while (($entry = readdir($dir)) !== false) {
$should_be = array_shift($expects);
$this->assertEquals($should_be, $entry);
}
$this->assertFalse(readdir($dir));
return $dir;
}
/**
* @depends testReaddir
*/
public function testRewindDir($dir)
{
$this->assertFalse(readdir($dir));
rewinddir($dir);
$this->assertEquals('bar/', readdir($dir));
return $dir;
}
/**
* @depends testRewindDir
*/
public function testCloseDir($dir)
{
$this->assertTrue(is_resource($dir));
closedir($dir);
// There is a bug in PHP where a
// resource buffer is not getting cleared.
// So this might return a value even though
// the underlying stream is cleared.
//$this->assertFalse(readdir($dir));
}
/**
* @depends testCloseDir
*/
public function testOpenSubdir()
{
// Opening foo we should find test2.txt and test3.txt.
$url = $this->newUrl('foo/');
$dir = opendir($url, $this->basicSwiftContext());
// I don't know why, but these are always returned in
// lexical order.
$this->assertEquals('test2.txt', readdir($dir));
$this->assertEquals('test3.txt', readdir($dir));
$array = scandir($url, -1, $this->basicSwiftContext());
$this->assertEquals(2, count($array));
$this->assertEquals('test3.txt', $array[0]);
}
}