diff options
author | diosmosis <benaka@piwik.pro> | 2014-09-18 07:40:38 +0400 |
---|---|---|
committer | diosmosis <benaka@piwik.pro> | 2014-09-18 07:40:38 +0400 |
commit | 41fabcb3488f00f5840dc2c231519488322b1837 (patch) | |
tree | 32021d167f45a4047e0195841fbb4fba8eed167f /tests/PHPUnit/Core | |
parent | aafb07c75b86b7905da1d7525e2800c8dbad2cde (diff) |
Adding new PivotByDimension DataTable filter that can pivot a report by (almost) any dimension. The filter can pivot reports by their subtable dimension and can also pivot by other dimensions (by using segments).
Notes:
- in the UI, only pivoting by subtable is supported
- change to CSV DataTable renderer so column names w/ commas & quotes can appear in text
- change to XML DataTable renderer so column names w/ invalid XML characters can be rendered (bit of an iffy change, XML format needs an overhaul I think)
- includes new config option 'pivot_by_filter_enable_fetch_by_segment'
- includes additions to component metadata classes (ie, Report/Dimension)
Diffstat (limited to 'tests/PHPUnit/Core')
-rw-r--r-- | tests/PHPUnit/Core/Columns/DimensionTest.php | 2 | ||||
-rw-r--r-- | tests/PHPUnit/Core/DataTable/Filter/PivotByDimensionTest.php | 351 | ||||
-rw-r--r-- | tests/PHPUnit/Core/DataTable/Renderer/CSVTest.php | 27 | ||||
-rw-r--r-- | tests/PHPUnit/Core/DataTable/Renderer/XMLTest.php | 54 | ||||
-rw-r--r-- | tests/PHPUnit/Core/Plugin/ComponentFactoryTest.php | 57 |
5 files changed, 489 insertions, 2 deletions
diff --git a/tests/PHPUnit/Core/Columns/DimensionTest.php b/tests/PHPUnit/Core/Columns/DimensionTest.php index 477ce7114a..6952b3880f 100644 --- a/tests/PHPUnit/Core/Columns/DimensionTest.php +++ b/tests/PHPUnit/Core/Columns/DimensionTest.php @@ -171,5 +171,5 @@ namespace Piwik\Tests\Core\Columns $dimension = Dimension::factory("ExampleTracker.ExampleDimension"); $this->assertInstanceOf("Piwik\\Plugins\\ExampleTracker\\Columns\\ExampleDimension", $dimension); } - } + } }
\ No newline at end of file diff --git a/tests/PHPUnit/Core/DataTable/Filter/PivotByDimensionTest.php b/tests/PHPUnit/Core/DataTable/Filter/PivotByDimensionTest.php new file mode 100644 index 0000000000..e20cdaf682 --- /dev/null +++ b/tests/PHPUnit/Core/DataTable/Filter/PivotByDimensionTest.php @@ -0,0 +1,351 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ +namespace Piwik\Tests\Core\DataTable\Filter; + +use Piwik\API\Proxy; +use Piwik\DataTable; +use Piwik\DataTable\Filter\PivotByDimension; +use Piwik\DataTable\Row; +use Piwik\Plugin\Manager as PluginManager; +use PHPUnit_Framework_TestCase; +use Exception; + +/** + * @group Core + * @group PivotByDimensionTest + */ +class PivotByDimensionTest extends PHPUnit_Framework_TestCase +{ + /** + * The number of segment tables that have been created. Used when injecting API results to make sure each + * segment table is different. + * + * @var int + */ + private $segmentTableCount; + + public function setUp() + { + $self = $this; + + $proxyMock = $this->getMock('stdClass', array('call')); + $proxyMock->expects($this->any())->method('call')->willReturnCallback(function ($className, $methodName, $parameters) use ($self) { + if ($className == "\\Piwik\\Plugins\\UserCountry\\API" + && $methodName == 'getCity' + ) { + return $self->getSegmentTable(); + } else { + throw new Exception("Unknown API request: $className::$methodName."); + } + }); + Proxy::setSingletonInstance($proxyMock); + + $this->segmentTableCount = 0; + } + + public function tearDown() + { + PluginManager::unsetInstance(); + Proxy::unsetInstance(); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage Unsupported pivot: report 'ExampleReport.ExampleReportName' has no subtable dimension. + */ + public function testConstructionFailsWhenReportHasNoSubtableAndSegmentFetchingIsDisabled() + { + PluginManager::getInstance()->loadPlugins(array('ExampleReport', 'UserCountry')); + + new PivotByDimension(new DataTable(), "ExampleReport.GetExampleReport", "UserCountry.City", 'nb_visits', $columnLimit = -1, $enableFetchBySegment = false); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage Unsupported pivot: the subtable dimension for 'Referrers.Referrers_Keywords' does not match the requested pivotBy dimension. + */ + public function testConstructionFailsWhenDimensionIsNotSubtableAndSegmentFetchingIsDisabled() + { + PluginManager::getInstance()->loadPlugins(array('Referrers', 'UserCountry')); + + new PivotByDimension(new DataTable(), "Referrers.getKeywords", "UserCountry.City", "nb_visits", $columnLimit = -1, $enableFetchBySegment = false); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage Unsupported pivot: No segment for dimension of report 'UserSettings.UserSettings_WidgetBrowserFamilies' + */ + public function testConstructionFailsWhenDimensionIsNotSubtableAndSegmentFetchingIsEnabledButThereIsNoSegment() + { + PluginManager::getInstance()->loadPlugins(array('Referrers', 'UserSettings')); + + new PivotByDimension(new DataTable(), "UserSettings.getBrowserType", "Referrers.Keyword", "nb_visits"); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage Invalid dimension 'ExampleTracker.InvalidDimension' + */ + public function testConstructionFailsWhenDimensionDoesNotExist() + { + PluginManager::getInstance()->loadPlugins(array('ExampleReport', 'ExampleTracker')); + + new PivotByDimension(new DataTable(), "ExampleReport.GetExampleReport", "ExampleTracker.InvalidDimension", 'nb_visits'); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage Unsupported pivot: No report for pivot dimension 'ExampleTracker.ExampleDimension' + */ + public function testConstructionFailsWhenThereIsNoReportForADimension() + { + PluginManager::getInstance()->loadPlugins(array('ExampleReport', 'ExampleTracker')); + + new PivotByDimension(new DataTable(), "ExampleReport.GetExampleReport", "ExampleTracker.ExampleDimension", "nb_visits"); + } + + /** + * @expectedException Exception + * @expectedExceptionMessage Unable to find report 'ExampleReport.InvalidReport' + */ + public function testConstructionFailsWhenSpecifiedReportIsNotValid() + { + PluginManager::getInstance()->loadPlugins(array('ExampleReport', 'Referrers')); + + new PivotByDimension(new DataTable(), "ExampleReport.InvalidReport", "Referrers.Keyword", "nb_visits"); + } + + public function testFilterReturnsEmptyResultWhenTableToFilterIsEmpty() + { + PluginManager::getInstance()->loadPlugins(array('Referrers', 'UserCountry', 'CustomVariables')); + + $table = new DataTable(); + + $pivotFilter = new PivotByDimension($table, "Referrers.getKeywords", "Referrers.SearchEngine", 'nb_visits'); + $pivotFilter->filter($table); + + $expectedRows = array(); + $this->assertEquals($expectedRows, $table->getRows()); + } + + public function testFilterCorrectlyCreatesPivotTableUsingSubtableReport() + { + PluginManager::getInstance()->loadPlugins(array('Referrers', 'UserCountry', 'CustomVariables')); + + $table = $this->getTableToFilter(true); + + $pivotFilter = new PivotByDimension($table, "Referrers.getKeywords", "Referrers.SearchEngine", 'nb_actions', $columnLimit = -1, $fetchBySegment = false); + $pivotFilter->filter($table); + + $expectedRows = array( + array('label' => 'row 1', 'col 1' => 2, 'col 2' => false, 'col 3' => false, 'col 4' => false), + array('label' => 'row 2', 'col 1' => 4, 'col 2' => 6, 'col 3' => false, 'col 4' => false), + array('label' => 'row 3', 'col 1' => false, 'col 2' => 8, 'col 3' => 31, 'col 4' => 33) + ); + $this->assertTableRowsEquals($expectedRows, $table); + } + + public function testFilterCorrectlyCreatesPivotTableUsingSegment() + { + PluginManager::getInstance()->loadPlugins(array('Referrers', 'UserCountry', 'CustomVariables')); + + $table = $this->getTableToFilter(true); + + $pivotFilter = new PivotByDimension($table, "Referrers.getKeywords", "UserCountry.City", 'nb_visits'); + $pivotFilter->filter($table); + + $expectedRows = array( + array('label' => 'row 1', 'col 0' => 2, 'col 1' => false, 'col 2' => false), + array('label' => 'row 2', 'col 0' => 2, 'col 1' => 4, 'col 2' => false), + array('label' => 'row 3', 'col 0' => 2, 'col 1' => 4, 'col 2' => 6) + ); + $this->assertTableRowsEquals($expectedRows, $table); + } + + public function testFilterCorrectlyCreatesPivotTableWhenPivotMetricDoesNotExistInTable() + { + PluginManager::getInstance()->loadPlugins(array('Referrers', 'UserCountry', 'CustomVariables')); + + $table = $this->getTableToFilter(true); + + $pivotFilter = new PivotByDimension($table, "Referrers.getKeywords", "Referrers.SearchEngine", 'invalid_metric'); + $pivotFilter->filter($table); + + $expectedRows = array( + array('label' => 'row 1', 'col 1' => false, 'col 2' => false, 'col 3' => false, 'col 4' => false), + array('label' => 'row 2', 'col 1' => false, 'col 2' => false, 'col 3' => false, 'col 4' => false), + array('label' => 'row 3', 'col 1' => false, 'col 2' => false, 'col 3' => false, 'col 4' => false) + ); + $this->assertTableRowsEquals($expectedRows, $table); + } + + public function testFilterCorrectlyCreatesPivotTableWhenSubtablesHaveNoRows() + { + PluginManager::getInstance()->loadPlugins(array('Referrers', 'UserCountry', 'CustomVariables')); + + $table = $this->getTableToFilter(false); + + $pivotFilter = new PivotByDimension($table, "CustomVariables.getCustomVariables", "CustomVariables.CustomVariableValue", + 'nb_visits', $fetchBySegment = false); + $pivotFilter->filter($table); + + $expectedRows = array( + array('label' => 'row 1'), + array('label' => 'row 2'), + array('label' => 'row 3') + ); + $this->assertTableRowsEquals($expectedRows, $table); + } + + public function testFilterCorrectlyDefaultsPivotByColumnWhenNoneProvided() + { + PluginManager::getInstance()->loadPlugins(array('Referrers', 'UserCountry', 'CustomVariables')); + + $table = $this->getTableToFilter(true); + + $pivotFilter = new PivotByDimension($table, "Referrers.getKeywords", "Referrers.SearchEngine", $column = false, $columnLimit = -1, $fetchBySegment = false); + $pivotFilter->filter($table); + + $expectedRows = array( + array('label' => 'row 1', 'col 1' => 1, 'col 2' => false, 'col 3' => false, 'col 4' => false), + array('label' => 'row 2', 'col 1' => 3, 'col 2' => 5, 'col 3' => false, 'col 4' => false), + array('label' => 'row 3', 'col 1' => false, 'col 2' => 7, 'col 3' => 9, 'col 4' => 32) + ); + $this->assertTableRowsEquals($expectedRows, $table); + } + + public function testFilterCorrectlyLimitsTheColumnNumberWhenColumnLimitProvided() + { + PluginManager::getInstance()->loadPlugins(array('Referrers', 'UserCountry', 'CustomVariables')); + + $table = $this->getTableToFilter(true); + + $pivotFilter = new PivotByDimension($table, "Referrers.getKeywords", "Referrers.SearchEngine", $column = 'nb_visits', $columnLimit = 3, $fetchBySegment = false); + $pivotFilter->filter($table); + + $expectedRows = array( + array('label' => 'row 1', 'col 2' => false, 'col 3' => false, 'col 4' => false), + array('label' => 'row 2', 'col 2' => 5, 'col 3' => false, 'col 4' => false), + array('label' => 'row 3', 'col 2' => 7, 'col 3' => 9, 'col 4' => 32) + ); + $this->assertTableRowsEquals($expectedRows, $table); + } + + private function getTableToFilter($addSubtables = false) + { + $row1 = new Row(array(Row::COLUMNS => array( + 'label' => 'row 1', + 'nb_visits' => 10, + 'nb_actions' => 15 + ))); + if ($addSubtables) { + $row1->setSubtable($this->getRow1Subtable()); + } + + $row2 = new Row(array(Row::COLUMNS => array( + 'label' => 'row 2', + 'nb_visits' => 13, + 'nb_actions' => 18 + ))); + if ($addSubtables) { + $row2->setSubtable($this->getRow2Subtable()); + } + + $row3 = new Row(array(Row::COLUMNS => array( + 'label' => 'row 3', + 'nb_visits' => 20, + 'nb_actions' => 25 + ))); + if ($addSubtables) { + $row3->setSubtable($this->getRow3Subtable()); + } + + $table = new DataTable(); + $table->addRowsFromArray(array($row1, $row2, $row3)); + return $table; + } + + private function getRow1Subtable() + { + $table = new DataTable(); + $table->addRowsFromArray(array( + new Row(array(Row::COLUMNS => array( + 'label' => 'col 1', + 'nb_visits' => 1, + 'nb_actions' => 2 + ))) + )); + return $table; + } + + private function getRow2Subtable() + { + $table = new DataTable(); + $table->addRowsFromArray(array( + new Row(array(Row::COLUMNS => array( + 'label' => 'col 1', + 'nb_visits' => 3, + 'nb_actions' => 4 + ))), + new Row(array(Row::COLUMNS => array( + 'label' => 'col 2', + 'nb_visits' => 5, + 'nb_actions' => 6 + ))) + )); + return $table; + } + + private function getRow3Subtable() + { + $table = new DataTable(); + $table->addRowsFromArray(array( + new Row(array(Row::COLUMNS => array( + 'label' => 'col 2', + 'nb_visits' => 7, + 'nb_actions' => 8 + ))), + new Row(array(Row::COLUMNS => array( + 'label' => 'col 3', + 'nb_visits' => 9, + 'nb_actions' => 31 + ))), + new Row(array(Row::COLUMNS => array( + 'label' => 'col 4', + 'nb_visits' => 32, + 'nb_actions' => 33 + ))) + )); + return $table; + } + + private function getSegmentTable() + { + ++$this->segmentTableCount; + + $table = new DataTable(); + for ($i = 0; $i != $this->segmentTableCount; ++$i) { + $row = new Row(array(Row::COLUMNS => array( + 'label' => 'col ' . $i, + 'nb_visits' => ($i + 1) * 2, + 'nb_actions' => ($i + 1) * 3 + ))); + $table->addRow($row); + } + return $table; + } + + private function assertTableRowsEquals($expectedRows, $table) + { + $renderer = new DataTable\Renderer\Php(); + $renderer->setSerialize(false); + $actualRows = $renderer->render($table); + + $this->assertEquals($expectedRows, $actualRows); + } +}
\ No newline at end of file diff --git a/tests/PHPUnit/Core/DataTable/Renderer/CSVTest.php b/tests/PHPUnit/Core/DataTable/Renderer/CSVTest.php index 4c5c77a355..64ffcf6a06 100644 --- a/tests/PHPUnit/Core/DataTable/Renderer/CSVTest.php +++ b/tests/PHPUnit/Core/DataTable/Renderer/CSVTest.php @@ -179,6 +179,22 @@ class DataTable_Renderer_CSVTest extends PHPUnit_Framework_TestCase } /** + * @group Core + */ + public function testCSVRendererCorrectlyEscapesHeadersAndValues() + { + $dataTable = $this->_getDataTableSimpleWithCommasInCells(); + $render = new Csv(); + $render->setTable($dataTable); + $render->convertToUnicode = false; + + $expected = '"col,1","col,2" +"val""1","val"",2"'; + $actual = $render->render(); + $this->assertEquals($expected, $actual); + } + + /** * DATA OF DATATABLE_ARRAY * ------------------------- */ @@ -440,4 +456,13 @@ b,d,f,g'; $this->assertEquals($expected, $render->render()); } -} + + private function _getDataTableSimpleWithCommasInCells() + { + $table = new DataTable(); + $table->addRowsFromSimpleArray(array( + array("col,1" => "val\"1", "col,2" => "val\",2") + )); + return $table; + } +}
\ No newline at end of file diff --git a/tests/PHPUnit/Core/DataTable/Renderer/XMLTest.php b/tests/PHPUnit/Core/DataTable/Renderer/XMLTest.php index 40596e0e23..40b58bdc13 100644 --- a/tests/PHPUnit/Core/DataTable/Renderer/XMLTest.php +++ b/tests/PHPUnit/Core/DataTable/Renderer/XMLTest.php @@ -11,6 +11,9 @@ use Piwik\DataTable\Renderer\Xml; use Piwik\DataTable\Row; use Piwik\DataTable\Simple; +/** + * @group Only + */ class DataTable_Renderer_XMLTest extends PHPUnit_Framework_TestCase { public function setUp() @@ -222,6 +225,39 @@ class DataTable_Renderer_XMLTest extends PHPUnit_Framework_TestCase } /** + * @group Core + */ + public function testXMLRendererSuccessfullyRendersWhenSimpleDataTableColumnsHaveInvalidXmlCharacters() + { + $dataTable = $this->_getDataTableSimpleWithInvalidChars(); + $render = new Xml(); + $render->setTable($dataTable); + $expected = '<?xml version="1.0" encoding="utf-8" ?> +<result> + <col name="$%@(%">1</col> + <col name="avbs$">2</col> + <col name="b/">2</col> +</result>'; + $this->assertEquals($expected, $render->render()); + } + + public function testXMLRendererSuccessfullyRendersWhenDataTableColumnsHaveInvalidXmlCharacters() + { + $dataTable = $this->_getDataTableWithInvalidChars(); + $render = new Xml(); + $render->setTable($dataTable); + $expected = '<?xml version="1.0" encoding="utf-8" ?> +<result> + <row> + <col name="$%@(%">1</col> + <col name="avbs$">2</col> + <col name="b/">2</col> + </row> +</result>'; + $this->assertEquals($expected, $render->render()); + } + + /** * DATA OF DATATABLE_ARRAY * ------------------------- */ @@ -601,4 +637,22 @@ class DataTable_Renderer_XMLTest extends PHPUnit_Framework_TestCase $this->assertEquals($expected, $render->render()); } + + private function _getDataTableSimpleWithInvalidChars() + { + $table = new DataTable\Simple(); + $table->addRowsFromSimpleArray( + array("$%@(%" => 1, "avbs$" => 2, "b/" => 2) + ); + return $table; + } + + private function _getDataTableWithInvalidChars() + { + $table = new DataTable(); + $table->addRowsFromSimpleArray( + array("$%@(%" => 1, "avbs$" => 2, "b/" => 2) + ); + return $table; + } } diff --git a/tests/PHPUnit/Core/Plugin/ComponentFactoryTest.php b/tests/PHPUnit/Core/Plugin/ComponentFactoryTest.php index d41149c8cd..b7b0f30fc3 100644 --- a/tests/PHPUnit/Core/Plugin/ComponentFactoryTest.php +++ b/tests/PHPUnit/Core/Plugin/ComponentFactoryTest.php @@ -11,6 +11,7 @@ use PHPUnit_Framework_TestCase; use Piwik\Config; use Piwik\Plugin\ComponentFactory; use Piwik\Plugin\Manager as PluginManager; +use Piwik\Plugin\Report; /** * @group Core @@ -74,6 +75,62 @@ class ComponentFactoryTest extends PHPUnit_Framework_TestCase $this->assertNull($report); } + public function test_getComponentIf_shouldNotFindAComponentIfComponentExistsButPluginIsNotLoaded() + { + $this->unloadAllPlugins(); + + $report = ComponentFactory::getComponentIf(self::REPORT_CLASS_NAME, 'ExampleReport', function (Report $report) { + return $report->getAction() == 'getExampleReport'; + }); + + $this->assertNull($report); + } + + public function test_getComponentIf_shouldFindAComponent_ThatExists() + { + $this->loadExampleReportPlugin(); + + $report = ComponentFactory::getComponentIf(self::REPORT_CLASS_NAME, 'ExampleReport', function (Report $report) { + return $report->getAction() == 'getExampleReport'; + }); + + $this->assertInstanceOf('Piwik\Plugins\ExampleReport\Reports\GetExampleReport', $report); + } + + public function test_getComponentIf_shouldNotFindAComponent_IfPluginIsActivatedButComponentNotExists() + { + $this->loadExampleReportPlugin(); + + $report = ComponentFactory::getComponentIf(self::REPORT_CLASS_NAME, 'ExampleReport', function (Report $report) { + return false; + }); + + $this->assertNull($report); + } + + public function test_getComponentIf_shouldNotFindAComponent_IfPluginIsLoadedButNotActivated() + { + PluginManager::getInstance()->loadPlugin('ExampleReport'); + + $report = ComponentFactory::getComponentIf(self::REPORT_CLASS_NAME, 'ExampleReport', function (Report $report) { + return $report->getAction() == 'getExampleReport'; + }); + + $this->assertNull($report); + } + + public function test_getComponentIf_shouldSearchThroughAllPlugins_IfNoPluginNameIsSupplied() + { + PluginManager::getInstance()->loadPlugins(array('ExampleReport', 'Referrers')); + + $reports = array(); + ComponentFactory::getComponentIf(self::REPORT_CLASS_NAME, null, function (Report $report) use (&$reports) { + $reports[] = $report; + }); + + $this->assertGreaterThan(1, count($reports)); + } + private function unloadAllPlugins() { PluginManager::getInstance()->loadPlugins(array()); |