First external release

This commit is contained in:
Adrien Loison committed 2015-01-15 18:14:07 -08:00
commit 5e199009e6
81 files changed
+5447

No files matched your search

+73
View File
@@ -0,0 +1,73 @@
<?php
namespace Box\Spout\Common\Escaper;
/**
* Class XLSXTest
*
* @package Box\Spout\Common\Escaper
*/
class XLSXTest extends \PHPUnit_Framework_TestCase
{
/**
* @return array
*/
public function dataProviderForTestEscape()
{
return [
['test', 'test'],
['adam\'s "car"', 'adam&#039;s &quot;car&quot;'],
[chr(0), '_x0000_'],
['_x0000_', '_x005F_x0000_'],
[chr(21), '_x0015_'],
['control '.chr(21).' character', 'control _x0015_ character'],
['control\'s '.chr(21).' "character"', 'control&#039;s _x0015_ &quot;character&quot;'],
];
}
/**
* @dataProvider dataProviderForTestEscape
*
* @param string $stringToEscape
* @param string $expectedEscapedString
* @return void
*/
public function testEscape($stringToEscape, $expectedEscapedString)
{
$escaper = new \Box\Spout\Common\Escaper\XLSX();
$escapedString = $escaper->escape($stringToEscape);
$this->assertEquals($expectedEscapedString, $escapedString, 'Incorrect escaped string');
}
/**
* @return array
*/
public function dataProviderForTestUnescape()
{
return [
['test', 'test'],
['adam&#039;s &quot;car&quot;', 'adam\'s "car"'],
['_x0000_', chr(0)],
['_x005F_x0000_', '_x0000_'],
['_x0015_', chr(21)],
['control _x0015_ character', 'control '.chr(21).' character'],
['control&#039;s _x0015_ &quot;character&quot;', 'control\'s '.chr(21).' "character"'],
];
}
/**
* @dataProvider dataProviderForTestUnescape
*
* @param string $stringToUnescape
* @param string $expectedUnescapedString
* @return void
*/
public function testUnescape($stringToUnescape, $expectedUnescapedString)
{
$escaper = new \Box\Spout\Common\Escaper\XLSX();
$unescapedString = $escaper->unescape($stringToUnescape);
$this->assertEquals($expectedUnescapedString, $unescapedString, 'Incorrect escaped string');
}
}
@@ -0,0 +1,59 @@
<?php
namespace Box\Spout\Common\Helper;
/**
* Class FileSystemHelperTest
*
* @package Box\Spout\Common\Helper
*/
class FileSystemHelperTest extends \PHPUnit_Framework_TestCase
{
/** @var \Box\Spout\Writer\Helper\XLSX\FileSystemHelper */
protected $fileSystemHelper;
/**
* @return void
*/
public function setUp()
{
$baseFolder = '/tmp/base_folder';
$this->fileSystemHelper = new FileSystemHelper($baseFolder);
}
/**
* @expectedException \Box\Spout\Common\Exception\IOException
* @return void
*/
public function testCreateFolderShouldThrowExceptionIfOutsideOfBaseFolder()
{
$this->fileSystemHelper->createFolder('/tmp/folder_outside_base_folder', 'folder_name');
}
/**
* @expectedException \Box\Spout\Common\Exception\IOException
* @return void
*/
public function testCreateFileWithContentsShouldThrowExceptionIfOutsideOfBaseFolder()
{
$this->fileSystemHelper->createFileWithContents('/tmp/folder_outside_base_folder', 'file_name', 'contents');
}
/**
* @expectedException \Box\Spout\Common\Exception\IOException
* @return void
*/
public function testDeleteFileShouldThrowExceptionIfOutsideOfBaseFolder()
{
$this->fileSystemHelper->deleteFile('/tmp/folder_outside_base_folder/file_name');
}
/**
* @expectedException \Box\Spout\Common\Exception\IOException
* @return void
*/
public function testDeleteFolderRecursivelyShouldThrowExceptionIfOutsideOfBaseFolder()
{
$this->fileSystemHelper->deleteFolderRecursively('/tmp/folder_outside_base_folder');
}
}
+158
View File
@@ -0,0 +1,158 @@
<?php
namespace Box\Spout\Reader;
use Box\Spout\Common\Type;
use Box\Spout\TestUsingResource;
/**
* Class CSVTest
*
* @package Box\Spout\Reader
*/
class CSVTest extends \PHPUnit_Framework_TestCase
{
use TestUsingResource;
/**
* @expectedException \Box\Spout\Common\Exception\IOException
*
* @return void
*/
public function testReadShouldThrowExceptionIfFileDoesNotExist()
{
$this->getAllRowsForFile('/path/to/fake/file.csv');
}
/**
* @return void
*/
public function testReadStandardCSV()
{
$allRows = $this->getAllRowsForFile('csv_standard.csv');
$expectedRows = [
['csv--11', 'csv--12', 'csv--13'],
['csv--21', 'csv--22', 'csv--23'],
['csv--31', 'csv--32', 'csv--33'],
];
$this->assertEquals($expectedRows, $allRows);
}
/**
* @return void
*/
public function testReadShouldNotStopAtCommaIfEnclosed()
{
$allRows = $this->getAllRowsForFile('csv_with_comma_enclosed.csv');
$this->assertEquals('This is, a comma', $allRows[0][0]);
}
/**
* @return void
*/
public function testReadShouldKeepEmptyCells()
{
$allRows = $this->getAllRowsForFile('csv_with_empty_cells.csv');
$expectedRows = [
['csv--11', 'csv--12', 'csv--13'],
['csv--21', '', 'csv--23'],
['csv--31', 'csv--32', ''],
];
$this->assertEquals($expectedRows, $allRows);
}
/**
* @return void
*/
public function testReadShouldSkipEmptyLines()
{
$allRows = $this->getAllRowsForFile('csv_with_empty_line.csv');
$expectedRows = [
['csv--11', 'csv--12', 'csv--13'],
['csv--31', 'csv--32', 'csv--33'],
];
$this->assertEquals($expectedRows, $allRows);
}
/**
* @return void
*/
public function testReadShouldHaveTheRightNumberOfCells()
{
$allRows = $this->getAllRowsForFile('csv_with_different_cells_number.csv');
$expectedRows = [
['csv--11', 'csv--12', 'csv--13'],
['csv--21', 'csv--22'],
['csv--31'],
];
$this->assertEquals($expectedRows, $allRows);
}
/**
* @return void
*/
public function testReadShouldSupportCustomFieldDelimiter()
{
$allRows = $this->getAllRowsForFile('csv_delimited_with_pipes.csv', '|');
$expectedRows = [
['csv--11', 'csv--12', 'csv--13'],
['csv--21', 'csv--22', 'csv--23'],
['csv--31', 'csv--32', 'csv--33'],
];
$this->assertEquals($expectedRows, $allRows);
}
/**
* @return void
*/
public function testReadShouldSupportCustomFieldEnclosure()
{
$allRows = $this->getAllRowsForFile('csv_text_enclosed_with_pound.csv', ',', '#');
$this->assertEquals('This is, a comma', $allRows[0][0]);
}
/**
* @return void
*/
public function testReadShouldSkipUtf8Bom()
{
$allRows = $this->getAllRowsForFile('csv_with_utf8_bom.csv');
$expectedRows = [
['csv--11', 'csv--12', 'csv--13'],
['csv--21', 'csv--22', 'csv--23'],
];
$this->assertEquals($expectedRows, $allRows);
}
/**
* @param string $fileName
* @param string|void $fieldDelimiter
* @param string|void $fieldEnclosure
* @return array All the read rows the given file
*/
private function getAllRowsForFile($fileName, $fieldDelimiter = ",", $fieldEnclosure = '"')
{
$allRows = [];
$resourcePath = $this->getResourcePath($fileName);
$reader = ReaderFactory::create(Type::CSV);
$reader->setFieldDelimiter($fieldDelimiter);
$reader->setFieldEnclosure($fieldEnclosure);
$reader->open($resourcePath);
while ($reader->hasNextRow()) {
$allRows[] = $reader->nextRow();
}
$reader->close();
return $allRows;
}
}
@@ -0,0 +1,99 @@
<?php
namespace Box\Spout\Reader\Helper\XLSX;
use Box\Spout\TestUsingResource;
/**
* Class SharedStringsHelperTest
*
* @package Box\Spout\Reader\Helper\XLSX
*/
class SharedStringsHelperTest extends \PHPUnit_Framework_TestCase
{
use TestUsingResource;
/** @var SharedStringsHelper */
private $sharedStringsHelper;
/**
* @return void
*/
public function setUp()
{
$resourcePath = $this->getResourcePath('one_sheet_with_shared_strings.xlsx');
$this->sharedStringsHelper = new SharedStringsHelper($resourcePath);
}
/**
* @return void
*/
public function tearDown()
{
$this->sharedStringsHelper->cleanup();
}
/**
* @return void
*/
public function testExtractSharedStringsShouldCreateTempFileWithSharedStrings()
{
$this->sharedStringsHelper->extractSharedStrings();
$tempFolder = \ReflectionHelper::getValueOnObject($this->sharedStringsHelper, 'tempFolder');
$filesInTempFolder = $this->getFilesInFolder($tempFolder);
$this->assertEquals(1, count($filesInTempFolder), 'One temp file should have been created in the temp folder.');
$tempFileContents = file_get_contents($filesInTempFolder[0]);
$tempFileContentsPerLine = explode("\n", $tempFileContents);
$this->assertEquals('s1--A1', $tempFileContentsPerLine[0]);
$this->assertEquals('s1--E5', $tempFileContentsPerLine[24]);
}
/**
* Returns all files that are in the given folder.
* It does not include "." and ".." and is not recursive.
*
* @param string $folderPath
* @return array
*/
private function getFilesInFolder($folderPath)
{
$files = [];
$directoryIterator = new \DirectoryIterator($folderPath);
foreach ($directoryIterator as $fileInfo) {
if ($fileInfo->isFile()) {
$files[] = $fileInfo->getPathname();
}
}
return $files;
}
/**
* @expectedException \Box\Spout\Reader\Exception\SharedStringNotFoundException
* @return void
*/
public function testGetStringAtIndexShouldThrowExceptionIfStringNotFound()
{
$this->sharedStringsHelper->extractSharedStrings();
$this->sharedStringsHelper->getStringAtIndex(PHP_INT_MAX);
}
/**
* @return void
*/
public function testGetStringAtIndexShouldReturnTheCorrectStringIfFound()
{
$this->sharedStringsHelper->extractSharedStrings();
$sharedString = $this->sharedStringsHelper->getStringAtIndex(0);
$this->assertEquals('s1--A1', $sharedString);
$sharedString = $this->sharedStringsHelper->getStringAtIndex(24);
$this->assertEquals('s1--E5', $sharedString);
}
}
+192
View File
@@ -0,0 +1,192 @@
<?php
namespace Box\Spout\Reader;
use Box\Spout\Common\Type;
use Box\Spout\TestUsingResource;
/**
* Class XLSXTest
*
* @package Box\Spout\Reader
*/
class XLSXTest extends \PHPUnit_Framework_TestCase
{
use TestUsingResource;
/**
* @return array
*/
public function dataProviderForTestReadShouldThrowException()
{
return [
['/path/to/fake/file.xlsx'],
['file_with_no_sheets_in_content_types.xlsx'],
['file_corrupted.xlsx'],
];
}
/**
* @dataProvider dataProviderForTestReadShouldThrowException
* @expectedException \Box\Spout\Common\Exception\IOException
*
* @param string $filePath
* @return void
*/
public function testReadShouldThrowException($filePath)
{
$this->getAllRowsForFile($filePath);
}
/**
* @return array
*/
public function dataProviderForTestReadForAllWorksheets()
{
return [
['one_sheet_with_shared_strings.xlsx', 5, 5],
['one_sheet_with_inline_strings.xlsx', 5, 5],
['two_sheets_with_shared_strings.xlsx', 10, 5],
['two_sheets_with_inline_strings.xlsx', 10, 5]
];
}
/**
* @dataProvider dataProviderForTestReadForAllWorksheets
*
* @param string $resourceName
* @param int $expectedNumOfRows
* @param int $expectedNumOfCellsPerRow
* @return void
*/
public function testReadForAllWorksheets($resourceName, $expectedNumOfRows, $expectedNumOfCellsPerRow)
{
$allRows = $this->getAllRowsForFile($resourceName);
$this->assertEquals($expectedNumOfRows, count($allRows), "There should be $expectedNumOfRows rows");
foreach ($allRows as $row) {
$this->assertEquals($expectedNumOfCellsPerRow, count($row), "There should be $expectedNumOfCellsPerRow cells for every row");
}
}
/**
* @return void
*/
public function testReadShouldKeepEmptyCellsAtTheEndIfDimensionsSpecified()
{
$allRows = $this->getAllRowsForFile('sheet_without_dimensions_but_spans_and_empty_cells.xlsx');
$this->assertEquals(2, count($allRows), 'There should be 2 rows');
foreach ($allRows as $row) {
$this->assertEquals(5, count($row), 'There should be 5 cells for every row, because empty rows should be preserved');
}
$expectedRows = [
['s1--A1', 's1--B1', 's1--C1', 's1--D1', 's1--E1'],
['s1--A2', 's1--B2', 's1--C2', '', ''],
];
$this->assertEquals($expectedRows, $allRows);
}
/**
* @return void
*/
public function testReadShouldKeepEmptyCellsAtTheEndIfNoDimensionsButSpansSpecified()
{
$allRows = $this->getAllRowsForFile('sheet_without_dimensions_and_empty_cells.xlsx');
$this->assertEquals(2, count($allRows), 'There should be 2 rows');
$this->assertEquals(5, count($allRows[0]), 'There should be 5 cells in the first row');
$this->assertEquals(3, count($allRows[1]), 'There should be only 3 cells in the second row, because empty rows at the end should be skip');
$expectedRows = [
['s1--A1', 's1--B1', 's1--C1', 's1--D1', 's1--E1'],
['s1--A2', 's1--B2', 's1--C2'],
];
$this->assertEquals($expectedRows, $allRows);
}
/**
* @return void
*/
public function testReadShouldSkipEmptyCellsAtTheEndIfDimensionsNotSpecified()
{
$allRows = $this->getAllRowsForFile('sheet_without_dimensions_and_empty_cells.xlsx');
$this->assertEquals(2, count($allRows), 'There should be 2 rows');
$this->assertEquals(5, count($allRows[0]), 'There should be 5 cells in the first row');
$this->assertEquals(3, count($allRows[1]), 'There should be only 3 cells in the second row, because empty rows at the end should be skip');
$expectedRows = [
['s1--A1', 's1--B1', 's1--C1', 's1--D1', 's1--E1'],
['s1--A2', 's1--B2', 's1--C2'],
];
$this->assertEquals($expectedRows, $allRows);
}
/**
* @return void
*/
public function testReadShouldSkipEmptyRows()
{
$allRows = $this->getAllRowsForFile('sheet_with_empty_rows.xlsx');
$this->assertEquals(2, count($allRows), 'There should be only 2 rows, because the empty row is skipped');
$expectedRows = [
['s1--A1', 's1--B1', 's1--C1', 's1--D1', 's1--E1'],
['s1--A3', 's1--B3', 's1--C3', 's1--D3', 's1--E3'],
];
$this->assertEquals($expectedRows, $allRows);
}
/**
* @return void
*/
public function testReadShouldSkipPronunciationData()
{
$allRows = $this->getAllRowsForFile('sheet_with_pronunciation.xlsx');
$expectedRow = ['名前', '一二三四'];
$this->assertEquals($expectedRow, $allRows[0], 'Pronunciation data should be removed.');
}
/**
* @return void
*/
public function testReadShouldBeProtectedAgainstBillionLaughsAttack()
{
$allRows = $this->getAllRowsForFile('billion_laughs_test_file.xlsx');
$expectedMaxMemoryUsage = 10 * 1024 * 1024; // 10MB
$this->assertLessThan($expectedMaxMemoryUsage, memory_get_peak_usage(true), 'Entities should not be expanded and therefore consume all the memory.');
$expectedFirstRow = ['s1--A1', 's1--B1', 's1--C1', 's1--D1', 's1--E1'];
$this->assertEquals($expectedFirstRow, $allRows[0], 'Entities should be ignored when reading XML files.');
}
/**
* @param string $fileName
* @return array All the read rows the given file
*/
private function getAllRowsForFile($fileName)
{
$allRows = [];
$resourcePath = $this->getResourcePath($fileName);
$reader = ReaderFactory::create(Type::XLSX);
$reader->open($resourcePath);
while ($reader->hasNextSheet()) {
$reader->nextSheet();
while ($reader->hasNextRow()) {
$allRows[] = $reader->nextRow();
}
}
$reader->close();
return $allRows;
}
}
+92
View File
@@ -0,0 +1,92 @@
<?php
/**
* Utility class for making PHP reflection easier to use.
*/
class ReflectionHelper
{
private static $privateVarsToReset = array();
/**
* Resets any static vars that were set to their
* original values (to not screw up later unit test runs).
*
* @return void
*/
public static function reset()
{
foreach (self::$privateVarsToReset as $class => $valueNames) {
foreach ($valueNames as $valueName => $originalValue) {
self::setStaticValue($class, $valueName, $originalValue, $saveOriginalValue = false);
}
}
self::$privateVarsToReset = array();
}
/**
* Get the value of a static private or public class property.
* Used to test internals of class without having to make the property public
*
* @param string $class
* @param string $valueName
* @return mixed|null
*/
public static function getStaticValue($class, $valueName)
{
$reflectionClass = new ReflectionClass($class);
$reflectionProperty = $reflectionClass->getProperty($valueName);
$reflectionProperty->setAccessible(true);
$value = $reflectionProperty->getValue();
// clean up
$reflectionProperty->setAccessible(false);
return $value;
}
/**
* Set the value of a static private or public class property.
* Used to test internals of class without having to make the property public
*
* @param string $class
* @param string $valueName
* @param mixed|null $value
* @param bool|void $saveOriginalValue
* @return void
*/
public static function setStaticValue($class, $valueName, $value, $saveOriginalValue = true)
{
$reflectionClass = new ReflectionClass($class);
$reflectionProperty = $reflectionClass->getProperty($valueName);
$reflectionProperty->setAccessible(true);
// to prevent side-effects in later tests, we need to remember the original value and reset it on tear down
// @NOTE: we need to check isset in case the original value was null or array()
if ($saveOriginalValue && (!isset(self::$privateVarsToReset[$class]) || !isset(self::$privateVarsToReset[$class][$name]))) {
self::$privateVarsToReset[$class][$valueName] = $reflectionProperty->getValue();
}
$reflectionProperty->setValue($value);
// clean up
$reflectionProperty->setAccessible(false);
}
/**
* @param object $object
* @param string $valueName
*
* @return mixed|null
*/
public static function getValueOnObject($object, $valueName)
{
$reflectionObject = new ReflectionObject($object);
$reflectionProperty = $reflectionObject->getProperty($valueName);
$reflectionProperty->setAccessible(true);
$value = $reflectionProperty->getValue($object);
// clean up
$reflectionProperty->setAccessible(false);
return $value;
}
}
+83
View File
@@ -0,0 +1,83 @@
<?php
namespace Box\Spout;
/**
* Trait TestUsingResource
*
* @package Box\Spout
*/
trait TestUsingResource {
/** @var string Path to the test resources folder */
private $resourcesPath = 'tests/resources/';
/** @var string Path to the test generated resources folder */
private $generatedResourcesPath = 'tests/resources/generated/';
/** @var string Path to the test resources folder, that does not have writing permissions */
private $generatedUnwritableResourcesPath = 'tests/resources/generated/unwritable/';
/**
* @param string $resourceName
* @return string|null Path of the resource who matches the given name or null if resource not found
*/
protected function getResourcePath($resourceName)
{
$resourceType = pathinfo($resourceName, PATHINFO_EXTENSION);
$resourcePath = $this->resourcesPath . strtolower($resourceType) . '/' . $resourceName;
return (file_exists($resourcePath) ? $resourcePath : null);
}
/**
* @param string $resourceName
* @return string Path of the generated resource for the given name
*/
protected function getGeneratedResourcePath($resourceName)
{
$resourceType = pathinfo($resourceName, PATHINFO_EXTENSION);
$generatedResourcePath = $this->generatedResourcesPath . strtolower($resourceType) . '/' . $resourceName;
return $generatedResourcePath;
}
/**
* @param string $resourceName
* @return void
*/
protected function createGeneratedFolderIfNeeded($resourceName)
{
$resourceType = pathinfo($resourceName, PATHINFO_EXTENSION);
$generatedResourcePathForType = $this->generatedResourcesPath . strtolower($resourceType);
if (!file_exists($generatedResourcePathForType)) {
mkdir($generatedResourcePathForType, 0777, true);
}
}
/**
* @param string $resourceName
* @return string Path of the generated unwritable (because parent folder is read only) resource for the given name
*/
protected function getGeneratedUnwritableResourcePath($resourceName)
{
return $this->generatedUnwritableResourcesPath . $resourceName;
}
/**
* @return void
*/
protected function createUnwritableFolderIfNeeded()
{
if (!file_exists($this->generatedUnwritableResourcesPath)) {
// Make sure generated folder exists first
if (!file_exists($this->generatedResourcesPath)) {
mkdir($this->generatedResourcesPath, 0777, true);
}
// 0444 = read only
mkdir($this->generatedUnwritableResourcesPath, 0444, true);
}
}
}
+140
View File
@@ -0,0 +1,140 @@
<?php
namespace Box\Spout\Writer;
use Box\Spout\Common\Type;
use Box\Spout\TestUsingResource;
/**
* Class CSVTest
*
* @package Box\Spout\Writer
*/
class CSVTest extends \PHPUnit_Framework_TestCase
{
use TestUsingResource;
/**
* @expectedException \Box\Spout\Common\Exception\IOException
*/
public function testWriteShouldThrowExceptionIfCannotOpenFileForWriting()
{
$fileName = 'file_that_wont_be_written.csv';
$this->createUnwritableFolderIfNeeded($fileName);
$filePath = $this->getGeneratedUnwritableResourcePath($fileName);
$writer = WriterFactory::create(Type::CSV);
@$writer->openToFile($filePath);
$writer->addRow(['csv--11', 'csv--12']);
$writer->close();
}
/**
* @expectedException \Box\Spout\Writer\Exception\WriterNotOpenedException
*/
public function testWriteShouldThrowExceptionIfCallAddRowBeforeOpeningWriter()
{
$writer = WriterFactory::create(Type::CSV);
$writer->addRow(['csv--11', 'csv--12']);
$writer->close();
}
/**
* @expectedException \Box\Spout\Writer\Exception\WriterNotOpenedException
*/
public function testWriteShouldThrowExceptionIfCallAddRowsBeforeOpeningWriter()
{
$writer = WriterFactory::create(Type::CSV);
$writer->addRows([['csv--11', 'csv--12']]);
$writer->close();
}
/**
* @return void
*/
public function testWriteShouldAddUtf8Bom()
{
$allRows = [
['csv--11', 'csv--12'],
];
$writtenContent = $this->writeToCsvFileAndReturnWrittenContent($allRows, 'csv_with_utf8_bom.csv');
$this->assertContains(CSV::UTF8_BOM, $writtenContent, 'The CSV file should contain a UTF-8 BOM');
}
/**
* @return void
*/
public function testWriteShouldSupportNullValues()
{
$allRows = [
['csv--11', null, 'csv--13'],
];
$writtenContent = $this->writeToCsvFileAndReturnWrittenContent($allRows, 'csv_with_null_values.csv');
$writtenContent = $this->trimWrittenContent($writtenContent);
$this->assertEquals('csv--11,,csv--13', $writtenContent, 'The null values should be replaced by empty values');
}
/**
* @return void
*/
public function testWriteShouldSupportCustomFieldDelimiter()
{
$allRows = [
['csv--11', 'csv--12', 'csv--13'],
['csv--21', 'csv--22', 'csv--23'],
];
$writtenContent = $this->writeToCsvFileAndReturnWrittenContent($allRows, 'csv_with_pipe_delimiters.csv', '|');
$writtenContent = $this->trimWrittenContent($writtenContent);
$this->assertEquals("csv--11|csv--12|csv--13\ncsv--21|csv--22|csv--23", $writtenContent, 'The fields should be delimited with |');
}
/**
* @return void
*/
public function testWriteShouldSupportCustomFieldEnclosure()
{
$allRows = [
['This is, a comma', 'csv--12', 'csv--13'],
];
$writtenContent = $this->writeToCsvFileAndReturnWrittenContent($allRows, 'csv_with_pound_enclosures.csv', ',', '#');
$writtenContent = $this->trimWrittenContent($writtenContent);
$this->assertEquals('#This is, a comma#,csv--12,csv--13', $writtenContent, 'The fields should be enclosed with #');
}
/**
* @param array $allRows
* @param string $fileName
* @param string $fieldDelimiter
* @param string $fieldEnclosure
* @return null|string
*/
private function writeToCsvFileAndReturnWrittenContent($allRows, $fileName, $fieldDelimiter = ',', $fieldEnclosure = '"')
{
$this->createGeneratedFolderIfNeeded($fileName);
$resourcePath = $this->getGeneratedResourcePath($fileName);
$writer = WriterFactory::create(Type::CSV);
$writer->setFieldDelimiter($fieldDelimiter);
$writer->setFieldEnclosure($fieldEnclosure);
$writer->openToFile($resourcePath);
$writer->addRows($allRows);
$writer->close();
return file_get_contents($resourcePath);
}
/**
* @param string $writtenContent
* @return string
*/
private function trimWrittenContent($writtenContent)
{
// remove line feeds and UTF-8 BOM
return trim($writtenContent, "\n" . CSV::UTF8_BOM);
}
}
@@ -0,0 +1,37 @@
<?php
namespace Box\Spout\Writer\Helper\XLSX;
/**
* Class CellHelperTest
*
* @package Box\Spout\Writer\Helper\XLSX
*/
class CellHelperTest extends \PHPUnit_Framework_TestCase
{
/**
* @return array
*/
public function dataProviderForTestGetCellIndexFromColumnIndex()
{
return [
[0, 'A'],
[1, 'B'],
[25, 'Z'],
[26, 'AA'],
[28, 'AC'],
];
}
/**
* @dataProvider dataProviderForTestGetCellIndexFromColumnIndex
*
* @param int $columnIndex
* @param string $expectedCellIndex
* @return void
*/
public function testGetCellIndexFromColumnIndex($columnIndex, $expectedCellIndex)
{
$this->assertEquals($expectedCellIndex, CellHelper::getCellIndexFromColumnIndex($columnIndex));
}
}
+62
View File
@@ -0,0 +1,62 @@
<?php
namespace Box\Spout\Writer;
use Box\Spout\Common\Type;
use Box\Spout\TestUsingResource;
/**
* Class SheetTest
*
* @package Box\Spout\Writer
*/
class SheetTest extends \PHPUnit_Framework_TestCase
{
use TestUsingResource;
/**
* @return void
*/
public function testGetSheetNumber()
{
$sheets = $this->writeDataAndReturnSheets('test_get_sheet_number.xlsx');
$this->assertEquals(2, count($sheets), '2 sheets should have been created');
$this->assertEquals(0, $sheets[0]->getSheetNumber(), 'The first sheet should be number 0');
$this->assertEquals(1, $sheets[1]->getSheetNumber(), 'The second sheet should be number 1');
}
/**
* @return void
*/
public function testGetSheetName()
{
$sheets = $this->writeDataAndReturnSheets('test_get_sheet_name.xlsx');
$this->assertEquals(2, count($sheets), '2 sheets should have been created');
$this->assertEquals('Sheet1', $sheets[0]->getName(), 'Invalid name for the first sheet');
$this->assertEquals('Sheet2', $sheets[1]->getName(), 'Invalid name for the second sheet');
}
/**
* @param string $fileName
* @return Sheet[]
*/
private function writeDataAndReturnSheets($fileName)
{
$this->createGeneratedFolderIfNeeded($fileName);
$resourcePath = $this->getGeneratedResourcePath($fileName);
/** @var \Box\Spout\Writer\XLSX $writer */
$writer = WriterFactory::create(Type::XLSX);
$writer->openToFile($resourcePath);
$writer->addRow(['xlsx--sheet1--11', 'xlsx--sheet1--12']);
$writer->addNewSheetAndMakeItCurrent();
$writer->addRow(['xlsx--sheet2--11', 'xlsx--sheet2--12', 'xlsx--sheet2--13']);
$writer->close();
return $writer->getSheets();
}
}
+417
View File
@@ -0,0 +1,417 @@
<?php
namespace Box\Spout\Writer;
use Box\Spout\Common\Type;
use Box\Spout\TestUsingResource;
/**
* Class XLSXTest
*
* @package Box\Spout\Writer
*/
class XLSXTest extends \PHPUnit_Framework_TestCase
{
use TestUsingResource;
/**
* @expectedException \Box\Spout\Common\Exception\IOException
*/
public function testAddRowShouldThrowExceptionIfCannotOpenAFileForWriting()
{
$fileName = 'file_that_wont_be_written.xlsx';
$this->createUnwritableFolderIfNeeded($fileName);
$filePath = $this->getGeneratedUnwritableResourcePath($fileName);
$writer = WriterFactory::create(Type::XLSX);
@$writer->openToFile($filePath);
$writer->addRow(['xlsx--11', 'xlsx--12']);
$writer->close();
}
/**
* @expectedException \Box\Spout\Writer\Exception\WriterNotOpenedException
*/
public function testAddRowShouldThrowExceptionIfCallAddRowBeforeOpeningWriter()
{
$writer = WriterFactory::create(Type::XLSX);
$writer->addRow(['xlsx--11', 'xlsx--12']);
$writer->close();
}
/**
* @expectedException \Box\Spout\Writer\Exception\WriterNotOpenedException
*/
public function testAddRowShouldThrowExceptionIfCallAddRowsBeforeOpeningWriter()
{
$writer = WriterFactory::create(Type::XLSX);
$writer->addRows([['xlsx--11', 'xlsx--12']]);
$writer->close();
}
/**
* @return void
*/
public function testAddNewSheetAndMakeItCurrent()
{
$fileName = 'test_add_new_sheet_and_make_it_current.xlsx';
$this->createGeneratedFolderIfNeeded($fileName);
$resourcePath = $this->getGeneratedResourcePath($fileName);
$writer = WriterFactory::create(Type::XLSX);
$writer->openToFile($resourcePath);
$writer->addNewSheetAndMakeItCurrent();
$writer->close();
$sheets = $writer->getSheets();
$this->assertEquals(2, count($sheets), 'There should be 2 sheets');
$this->assertEquals($sheets[1], $writer->getCurrentSheet(), 'The current sheet should be the second one.');
}
/**
* @return void
*/
public function testSetCurrentSheet()
{
$fileName = 'test_set_current_sheet.xlsx';
$this->createGeneratedFolderIfNeeded($fileName);
$resourcePath = $this->getGeneratedResourcePath($fileName);
$writer = WriterFactory::create(Type::XLSX);
$writer->openToFile($resourcePath);
$writer->addNewSheetAndMakeItCurrent();
$writer->addNewSheetAndMakeItCurrent();
$firstSheet = $writer->getSheets()[0];
$writer->setCurrentSheet($firstSheet);
$writer->close();
$this->assertEquals($firstSheet, $writer->getCurrentSheet(), 'The current sheet should be the first one.');
}
/**
* @return void
*/
public function testAddRowShouldWriteGivenDataToSheetUsingInlineStrings()
{
$fileName = 'test_add_row_should_write_given_data_to_sheet.xlsx';
$dataRows = [
['xlsx--11', 'xlsx--12'],
['xlsx--21', 'xlsx--22', 'xlsx--23'],
];
$this->writeToXLSXFile($dataRows, $fileName, $shouldUseInlineStrings = true);
foreach ($dataRows as $dataRow) {
foreach ($dataRow as $cellValue) {
$this->assertInlineStringWasWrittenToSheet($fileName, 1, $cellValue);
}
}
}
/**
* @return void
*/
public function testAddRowShouldWriteGivenDataToTwoSheetUsingInlineStrings()
{
$fileName = 'test_add_row_should_write_given_data_to_sheet.xlsx';
$dataRows = [
['xlsx--11', 'xlsx--12'],
['xlsx--21', 'xlsx--22', 'xlsx--23'],
];
$numSheets = 2;
$this->writeToMultipleSheetsInXLSXFile($dataRows, $numSheets, $fileName, $shouldUseInlineStrings = true);
for ($i = 1; $i <= $numSheets; $i++) {
foreach ($dataRows as $dataRow) {
foreach ($dataRow as $cellValue) {
$this->assertInlineStringWasWrittenToSheet($fileName, $numSheets, $cellValue);
}
}
}
}
/**
* @return void
*/
public function testAddRowShouldWriteGivenDataToSheetUsingSharedStrings()
{
$fileName = 'test_add_row_should_write_given_data_to_sheet.xlsx';
$dataRows = [
['xlsx--11', 'xlsx--12'],
['xlsx--21', 'xlsx--22', 'xlsx--23'],
];
$this->writeToXLSXFile($dataRows, $fileName, $shouldUseInlineStrings = false);
foreach ($dataRows as $dataRow) {
foreach ($dataRow as $cellValue) {
$this->assertSharedStringWasWritten($fileName, $cellValue);
}
}
}
/**
* @return void
*/
public function testAddRowShouldWriteGivenDataToTwoSheetUsingSharedStrings()
{
$fileName = 'test_add_row_should_write_given_data_to_two_sheet_using_shared_strings.xlsx';
$dataRows = [
['xlsx--11', 'xlsx--12'],
['xlsx--21', 'xlsx--22', 'xlsx--23'],
];
$numSheets = 2;
$this->writeToMultipleSheetsInXLSXFile($dataRows, $numSheets, $fileName, $shouldUseInlineStrings = false);
for ($i = 1; $i <= $numSheets; $i++) {
foreach ($dataRows as $dataRow) {
foreach ($dataRow as $cellValue) {
$this->assertSharedStringWasWritten($fileName, $cellValue);
}
}
}
}
/**
* @return void
*/
public function testAddRowShouldWriteGivenDataToTheCorrectSheet()
{
$fileName = 'test_add_row_should_write_given_data_to_the_correct_sheet.xlsx';
$dataRowsSheet1 = [
['xlsx--sheet1--11', 'xlsx--sheet1--12'],
['xlsx--sheet1--21', 'xlsx--sheet1--22', 'xlsx--sheet1--23'],
];
$dataRowsSheet2 = [
['xlsx--sheet2--11', 'xlsx--sheet2--12'],
['xlsx--sheet2--21', 'xlsx--sheet2--22', 'xlsx--sheet2--23'],
];
$dataRowsSheet1Again = [
['xlsx--sheet1--31', 'xlsx--sheet1--32'],
['xlsx--sheet1--41', 'xlsx--sheet1--42', 'xlsx--sheet1--43'],
];
$this->createGeneratedFolderIfNeeded($fileName);
$resourcePath = $this->getGeneratedResourcePath($fileName);
/** @var \Box\Spout\Writer\XLSX $writer */
$writer = WriterFactory::create(Type::XLSX);
$writer->setShouldUseInlineStrings(true);
$writer->openToFile($resourcePath);
$writer->addRows($dataRowsSheet1);
$writer->addNewSheetAndMakeItCurrent();
$writer->addRows($dataRowsSheet2);
$firstSheet = $writer->getSheets()[0];
$writer->setCurrentSheet($firstSheet);
$writer->addRows($dataRowsSheet1Again);
$writer->close();
foreach ($dataRowsSheet1 as $dataRow) {
foreach ($dataRow as $cellValue) {
$this->assertInlineStringWasWrittenToSheet($fileName, 1, $cellValue, 'Data should have been written in Sheet 1');
}
}
foreach ($dataRowsSheet2 as $dataRow) {
foreach ($dataRow as $cellValue) {
$this->assertInlineStringWasWrittenToSheet($fileName, 2, $cellValue, 'Data should have been written in Sheet 2');
}
}
foreach ($dataRowsSheet1Again as $dataRow) {
foreach ($dataRow as $cellValue) {
$this->assertInlineStringWasWrittenToSheet($fileName, 1, $cellValue, 'Data should have been written in Sheet 1');
}
}
}
/**
* @return void
*/
public function testAddRowShouldAutomaticallyCreateNewSheetsIfMaxRowsReachedAndOptionTurnedOn()
{
$fileName = 'test_add_row_should_automatically_create_new_sheets_if_max_rows_reached_and_option_turned_on.xlsx';
$dataRows = [
['xlsx--sheet1--11', 'xlsx--sheet1--12'],
['xlsx--sheet1--21', 'xlsx--sheet1--22', 'xlsx--sheet1--23'],
['xlsx--sheet2--11', 'xlsx--sheet2--12'], // this should be written in a new sheet
];
// set the maxRowsPerSheet limit to 2
\ReflectionHelper::setStaticValue('\Box\Spout\Writer\Internal\XLSX\Workbook', 'maxRowsPerWorksheet', 2);
$writer = $this->writeToXLSXFile($dataRows, $fileName, true, $shouldCreateSheetsAutomatically = true);
$this->assertEquals(2, count($writer->getSheets()), '2 sheets should have been created.');
$this->assertInlineStringWasNotWrittenToSheet($fileName, 1, 'xlsx--sheet2--11');
$this->assertInlineStringWasWrittenToSheet($fileName, 2, 'xlsx--sheet2--11');
\ReflectionHelper::reset();
}
/**
* @return void
*/
public function testAddRowShouldNotCreateNewSheetsIfMaxRowsReachedAndOptionTurnedOff()
{
$fileName = 'test_add_row_should_not_create_new_sheets_if_max_rows_reached_and_option_turned_off.xlsx';
$dataRows = [
['xlsx--sheet1--11', 'xlsx--sheet1--12'],
['xlsx--sheet1--21', 'xlsx--sheet1--22', 'xlsx--sheet1--23'],
['xlsx--sheet1--31', 'xlsx--sheet1--32'], // this should NOT be written in a new sheet
];
// set the maxRowsPerSheet limit to 2
\ReflectionHelper::setStaticValue('\Box\Spout\Writer\Internal\XLSX\Workbook', 'maxRowsPerWorksheet', 2);
$writer = $this->writeToXLSXFile($dataRows, $fileName, true, $shouldCreateSheetsAutomatically = false);
$this->assertEquals(1, count($writer->getSheets()), 'Only 1 sheet should have been created.');
$this->assertInlineStringWasNotWrittenToSheet($fileName, 1, 'xlsx--sheet1--31');
\ReflectionHelper::reset();
}
/**
* @return void
*/
public function testAddRowShouldEscapeHtmlSpecialCharacters()
{
$fileName = 'test_add_row_should_escape_html_special_characters.xlsx';
$dataRows = [
['I\'m in "great" mood', 'This <must> be escaped & tested'],
];
$this->writeToXLSXFile($dataRows, $fileName);
$this->assertInlineStringWasWrittenToSheet($fileName, 1, 'I&#039;m in &quot;great&quot; mood', 'Quotes should be escaped');
$this->assertInlineStringWasWrittenToSheet($fileName, 1, 'This &lt;must&gt; be escaped &amp; tested', '<, > and & should be escaped');
}
/**
* @return void
*/
public function testAddRowShouldEscapeControlCharacters()
{
$fileName = 'test_add_row_should_escape_html_special_characters.xlsx';
$dataRows = [
['control\'s '.chr(21).' "character"'],
];
$this->writeToXLSXFile($dataRows, $fileName);
$this->assertInlineStringWasWrittenToSheet($fileName, 1, 'control&#039;s _x0015_ &quot;character&quot;');
}
/**
* @param array $allRows
* @param string $fileName
* @param bool $shouldUseInlineStrings
* @param bool $shouldCreateSheetsAutomatically
* @return XLSX
*/
private function writeToXLSXFile($allRows, $fileName, $shouldUseInlineStrings = true, $shouldCreateSheetsAutomatically = true)
{
$this->createGeneratedFolderIfNeeded($fileName);
$resourcePath = $this->getGeneratedResourcePath($fileName);
/** @var \Box\Spout\Writer\XLSX $writer */
$writer = WriterFactory::create(Type::XLSX);
$writer->setShouldUseInlineStrings($shouldUseInlineStrings);
$writer->setShouldCreateNewSheetsAutomatically($shouldCreateSheetsAutomatically);
$writer->openToFile($resourcePath);
$writer->addRows($allRows);
$writer->close();
return $writer;
}
/**
* @param array $allRows
* @param int $numSheets
* @param string $fileName
* @param bool $shouldUseInlineStrings
* @param bool $shouldCreateSheetsAutomatically
* @return XLSX
*/
private function writeToMultipleSheetsInXLSXFile($allRows, $numSheets, $fileName, $shouldUseInlineStrings = true, $shouldCreateSheetsAutomatically = true)
{
$this->createGeneratedFolderIfNeeded($fileName);
$resourcePath = $this->getGeneratedResourcePath($fileName);
/** @var \Box\Spout\Writer\XLSX $writer */
$writer = WriterFactory::create(Type::XLSX);
$writer->setShouldUseInlineStrings($shouldUseInlineStrings);
$writer->setShouldCreateNewSheetsAutomatically($shouldCreateSheetsAutomatically);
$writer->openToFile($resourcePath);
$writer->addRows($allRows);
for ($i = 1; $i < $numSheets; $i++) {
$writer->addNewSheetAndMakeItCurrent();
$writer->addRows($allRows);
}
$writer->close();
return $writer;
}
/**
* @param string $fileName
* @param int $sheetNumber
* @param string $inlineString
* @param string $message
* @return void
*/
private function assertInlineStringWasWrittenToSheet($fileName, $sheetNumber, $inlineString, $message = '')
{
$resourcePath = $this->getGeneratedResourcePath($fileName);
$pathToSheetFile = $resourcePath . '#xl/worksheets/sheet' . $sheetNumber . '.xml';
$xmlContents = file_get_contents('zip://' . $pathToSheetFile);
$this->assertContains($inlineString, $xmlContents, $message);
}
/**
* @param string $fileName
* @param int $sheetNumber
* @param string $inlineString
* @param string $message
* @return void
*/
private function assertInlineStringWasNotWrittenToSheet($fileName, $sheetNumber, $inlineString, $message = '')
{
$resourcePath = $this->getGeneratedResourcePath($fileName);
$pathToSheetFile = $resourcePath . '#xl/worksheets/sheet' . $sheetNumber . '.xml';
$xmlContents = file_get_contents('zip://' . $pathToSheetFile);
$this->assertNotContains($inlineString, $xmlContents, $message);
}
/**
* @param string $fileName
* @param string $sharedString
* @param string $message
* @return void
*/
private function assertSharedStringWasWritten($fileName, $sharedString, $message = '')
{
$resourcePath = $this->getGeneratedResourcePath($fileName);
$pathToSharedStringsFile = $resourcePath . '#xl/sharedStrings.xml';
$xmlContents = file_get_contents('zip://' . $pathToSharedStringsFile);
$this->assertContains($sharedString, $xmlContents, $message);
}
}