Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Steur <thomas.steur@gmail.com>2015-03-05 07:35:00 +0300
committerThomas Steur <thomas.steur@gmail.com>2015-03-31 05:27:04 +0300
commit1a148fb681762d7177b7bda0fa852bb06492b79b (patch)
tree50dfb7046f5311aa945ff46df89ad4f72aa0837c /tests/PHPUnit
parentb8db68ba80550f509d8a1d2cf4e390a584f324b0 (diff)
Faster archiving of aggregated reports, also performance imprvovements in general
* Store subtables in chunks of 100 subtables per blob. Those 100 subtables are stored serialized as an array: array($subtableID => subtableBlob). The first 100 subtables are stored in "chunk_0", the next 100 subtables are stored in "chunk_1", ... * Subtable Ids are now consecutive from 1 to X * We do no longer serialize the whole Row instance when archiving, instead we only serialize the Row's array which contains columns, metadata and datatable. This is not only more efficient but allows us to refactor the Row instance in the future (although we will always have to be BC) * Faster row implementation: Columns, Metadata and Subtables access is much faster now
Diffstat (limited to 'tests/PHPUnit')
-rw-r--r--tests/PHPUnit/Framework/Mock/FakeAccess.php4
-rw-r--r--tests/PHPUnit/Framework/Mock/Site.php30
-rw-r--r--tests/PHPUnit/Integration/ArchiveTest.php216
-rw-r--r--tests/PHPUnit/Integration/DbTest.php26
-rwxr-xr-xtests/PHPUnit/System/OneVisitorOneWebsiteSeveralDaysDateRangeArchivingTestsTest.php36
-rwxr-xr-xtests/PHPUnit/System/TwoVisitsWithCustomVariablesSegmentMatchVisitorTypeTest.php55
-rw-r--r--tests/PHPUnit/Unit/Archive/ChunkTest.php148
-rw-r--r--tests/PHPUnit/Unit/ArchiveProcessorTest.php129
-rw-r--r--tests/PHPUnit/Unit/DataAccess/ArchiveWriterTest.php110
-rw-r--r--tests/PHPUnit/Unit/DataTable/Filter/TruncateTest.php4
-rw-r--r--tests/PHPUnit/Unit/DataTable/ManagerTest.php244
-rw-r--r--tests/PHPUnit/Unit/DataTable/RowTest.php294
-rw-r--r--tests/PHPUnit/Unit/DataTableTest.php287
-rw-r--r--tests/PHPUnit/Unit/DbTest.php39
-rw-r--r--tests/PHPUnit/Unit/DeprecatedMethodsTest.php3
15 files changed, 1426 insertions, 199 deletions
diff --git a/tests/PHPUnit/Framework/Mock/FakeAccess.php b/tests/PHPUnit/Framework/Mock/FakeAccess.php
index 919df20961..1f3ff2c95d 100644
--- a/tests/PHPUnit/Framework/Mock/FakeAccess.php
+++ b/tests/PHPUnit/Framework/Mock/FakeAccess.php
@@ -8,7 +8,7 @@
namespace Piwik\Tests\Framework\Mock;
use Piwik\Plugins\SitesManager\API;
-use Piwik\Site;
+use Piwik\Site as PiwikSite;
use Exception;
/**
@@ -77,7 +77,7 @@ class FakeAccess
$websitesAccess = API::getInstance()->getAllSitesId();
}
- $idSites = Site::getIdSitesFromIdSitesString($idSites);
+ $idSites = PiwikSite::getIdSitesFromIdSitesString($idSites);
foreach ($idSites as $idsite) {
if (!in_array($idsite, $websitesAccess)) {
diff --git a/tests/PHPUnit/Framework/Mock/Site.php b/tests/PHPUnit/Framework/Mock/Site.php
new file mode 100644
index 0000000000..048eecff21
--- /dev/null
+++ b/tests/PHPUnit/Framework/Mock/Site.php
@@ -0,0 +1,30 @@
+<?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\Framework\Mock;
+
+/**
+ * FakeSite for UnitTests
+ * @since 2.13.0
+ */
+class Site extends \Piwik\Site
+{
+ /**
+ * Constructor.
+ *
+ * @param int $idsite The ID of the site we want data for.
+ */
+ public function __construct($idsite)
+ {
+ $this->id = (int)$idsite;
+ }
+
+ public function getTimezone()
+ {
+ return 'UTC';
+ }
+} \ No newline at end of file
diff --git a/tests/PHPUnit/Integration/ArchiveTest.php b/tests/PHPUnit/Integration/ArchiveTest.php
index b22a835f34..f217fdaca7 100644
--- a/tests/PHPUnit/Integration/ArchiveTest.php
+++ b/tests/PHPUnit/Integration/ArchiveTest.php
@@ -7,17 +7,31 @@
*/
namespace Piwik\Tests\Integration;
-use Piwik\Archive;
+use Piwik\Archive as PiwikArchive;
+use Piwik\ArchiveProcessor\Parameters;
use Piwik\ArchiveProcessor\Rules;
use Piwik\Common;
use Piwik\Config;
use Piwik\DataAccess\ArchiveTableCreator;
+use Piwik\DataAccess\ArchiveWriter;
use Piwik\Date;
use Piwik\Db;
use Piwik\Piwik;
+use Piwik\Segment;
+use Piwik\Site;
use Piwik\Tests\Fixtures\OneVisitorTwoVisits;
-use Piwik\Tests\Framework\Fixture;
use Piwik\Tests\Framework\TestCase\IntegrationTestCase;
+use Piwik\Period\Factory as PeriodFactory;
+use Piwik\Archive\Chunk;
+
+class Archive extends PiwikArchive
+{
+ public function get($archiveNames, $archiveDataType, $idSubtable = null)
+ {
+ return parent::get($archiveNames, $archiveDataType, $idSubtable);
+ }
+
+}
/**
* @group Core
@@ -101,6 +115,195 @@ class ArchiveTest extends IntegrationTestCase
$this->assertArchiveTablesAreNotEmpty('2010_03');
}
+ public function test_ArchiveBlob_ShouldBeAbleToLoadFirstLevelDataArrays()
+ {
+ $this->createManyDifferentArchiveBlobs();
+
+ $archive = $this->getArchive('day', '2013-01-01,2013-01-05');
+ $dataArrays = $archive->get(array('Actions_Actionsurl'), 'blob');
+
+ $this->assertArchiveBlob($dataArrays, '2013-01-01', array('Actions_Actionsurl' => 'test01'));
+ $this->assertArchiveBlob($dataArrays, '2013-01-02', array('Actions_Actionsurl' => 'test02'));
+ $this->assertArchiveBlob($dataArrays, '2013-01-03', array('Actions_Actionsurl' => 'test03'));
+ $this->assertArchiveBlob($dataArrays, '2013-01-04', array('Actions_Actionsurl' => 'test04'));
+ $this->assertArchiveBlob($dataArrays, '2013-01-05', array('Actions_Actionsurl' => 0));
+ }
+
+ public function test_ArchiveBlob_ShouldBeAbleToLoadOneSubtable_NoMatterWhetherTheyAreStoredSeparatelyOrInACombinedSubtableEntry()
+ {
+ $this->createManyDifferentArchiveBlobs();
+
+ $archive = $this->getArchive('day', '2013-01-01,2013-01-05');
+ $dataArrays = $archive->get(array('Actions_Actionsurl'), 'blob', 2);
+
+ $this->assertArchiveBlob($dataArrays, '2013-01-01', array('Actions_Actionsurl_2' => 0));
+ $this->assertArchiveBlob($dataArrays, '2013-01-02', array('Actions_Actionsurl_2' => 'test2'));
+ $this->assertArchiveBlob($dataArrays, '2013-01-03', array('Actions_Actionsurl_2' => 'subtable2'));
+ $this->assertArchiveBlob($dataArrays, '2013-01-04', array('Actions_Actionsurl_2' => 0));
+ $this->assertArchiveBlob($dataArrays, '2013-01-05', array('Actions_Actionsurl_2' => 0));
+
+ // test another one
+ $dataArrays = $archive->get(array('Actions_Actionsurl'), 'blob', 5);
+
+ $this->assertArchiveBlob($dataArrays, '2013-01-01', array('Actions_Actionsurl_5' => 0));
+ $this->assertArchiveBlob($dataArrays, '2013-01-02', array('Actions_Actionsurl_5' => 0));
+ $this->assertArchiveBlob($dataArrays, '2013-01-03', array('Actions_Actionsurl_5' => 'subtable5'));
+ $this->assertArchiveBlob($dataArrays, '2013-01-04', array('Actions_Actionsurl_5' => 'subtable45'));
+ $this->assertArchiveBlob($dataArrays, '2013-01-05', array('Actions_Actionsurl_5' => 0));
+
+ // test one that does not exist
+ $dataArrays = $archive->get(array('Actions_Actionsurl'), 'blob', 999);
+
+ $this->assertArchiveBlob($dataArrays, '2013-01-01', array('Actions_Actionsurl_999' => 0));
+ $this->assertArchiveBlob($dataArrays, '2013-01-02', array('Actions_Actionsurl_999' => 0));
+ $this->assertArchiveBlob($dataArrays, '2013-01-03', array('Actions_Actionsurl_999' => 0));
+ $this->assertArchiveBlob($dataArrays, '2013-01-04', array('Actions_Actionsurl_999' => 0));
+ $this->assertArchiveBlob($dataArrays, '2013-01-05', array('Actions_Actionsurl_999' => 0));
+ }
+
+ public function test_ArchiveBlob_ShouldBeAbleToLoadAllSubtables_NoMatterWhetherTheyAreStoredSeparatelyOrInACombinedSubtableEntry()
+ {
+ $this->createManyDifferentArchiveBlobs();
+
+ $archive = $this->getArchive('day', '2013-01-01,2013-01-06');
+ $dataArrays = $archive->get(array('Actions_Actionsurl'), 'blob', Archive::ID_SUBTABLE_LOAD_ALL_SUBTABLES);
+
+ $this->assertArchiveBlob($dataArrays, '2013-01-01', array('Actions_Actionsurl' => 'test01'));
+ $this->assertArchiveBlob($dataArrays, '2013-01-02', array('Actions_Actionsurl' => 'test02', 'Actions_Actionsurl_1' => 'test1', 'Actions_Actionsurl_2' => 'test2'));
+ $this->assertArchiveBlob($dataArrays, '2013-01-03', array('Actions_Actionsurl' => 'test03', 'Actions_Actionsurl_1' => 'subtable1', 'Actions_Actionsurl_2' => 'subtable2', 'Actions_Actionsurl_5' => 'subtable5'));
+ $this->assertArchiveBlob($dataArrays, '2013-01-04', array('Actions_Actionsurl' => 'test04', 'Actions_Actionsurl_5' => 'subtable45', 'Actions_Actionsurl_6' => 'subtable6'));
+ $this->assertArchiveBlob($dataArrays, '2013-01-05', array('Actions_Actionsurl' => 0));
+ $this->assertArchiveBlob($dataArrays, '2013-01-06', array('Actions_Actionsurl' => 'test06'));
+ }
+
+ public function test_ArchiveBlob_ShouldBeAbleToLoadDifferentArchives_NoMatterWhetherTheyAreStoredSeparatelyOrInACombinedSubtableEntry()
+ {
+ $this->createManyDifferentArchiveBlobs();
+
+ $archive = $this->getArchive('day', '2013-01-01,2013-01-06');
+ $dataArrays = $archive->get(array('Actions_Actionsurl', 'Actions_Actions'), 'blob', 2);
+
+ $this->assertArchiveBlob($dataArrays, '2013-01-01', array('Actions_Actionsurl_2' => 0, 'Actions_Actions_2' => 0));
+ $this->assertArchiveBlob($dataArrays, '2013-01-02', array('Actions_Actions_2' => 'actionsSubtable2', 'Actions_Actionsurl_2' => 'test2'));
+ $this->assertArchiveBlob($dataArrays, '2013-01-03', array('Actions_Actions_2' => 'actionsTest2', 'Actions_Actionsurl_2' => 'subtable2'));
+ $this->assertArchiveBlob($dataArrays, '2013-01-04', array('Actions_Actionsurl_2' => 0, 'Actions_Actions_2' => 0));
+ $this->assertArchiveBlob($dataArrays, '2013-01-05', array('Actions_Actionsurl_2' => 0, 'Actions_Actions_2' => 0));
+ $this->assertArchiveBlob($dataArrays, '2013-01-06', array('Actions_Actionsurl_2' => 0, 'Actions_Actions_2' => 0));
+ }
+
+ /**
+ * @dataProvider findBlobsWithinDifferentChunksDataProvider
+ */
+ public function test_ArchiveBlob_ShouldBeFindBlobs_WithinDifferentChunks($idSubtable, $expectedBlob)
+ {
+ $chunk = new Chunk();
+ $chunk5 = $chunk->getBlobIdForTable($subtableId = 5);
+ $chunk152 = $chunk->getBlobIdForTable($subtableId = 152);
+ $chunk399 = $chunk->getBlobIdForTable($subtableId = 399);
+
+ $this->createArchiveBlobEntry('2013-01-02', array(
+ 'Actions_Actions' => array(
+ 0 => 'actions_02',
+ $chunk5 => serialize(array(1 => 'actionsSubtable1', 2 => 'actionsSubtable2', 5 => 'actionsSubtable5')),
+ $chunk152 => serialize(array(151 => 'actionsSubtable151', 152 => 'actionsSubtable152')),
+ $chunk399 => serialize(array(399 => 'actionsSubtable399')),
+ )
+ ));
+
+ $archive = $this->getArchive('day', '2013-01-02,2013-01-02');
+
+ $dataArrays = $archive->get(array('Actions_Actions'), 'blob', $idSubtable);
+ $this->assertArchiveBlob($dataArrays, '2013-01-02', $expectedBlob);
+ }
+
+ public function findBlobsWithinDifferentChunksDataProvider()
+ {
+ return array(
+ array($idSubtable = 2, $expectedBlobs = array('Actions_Actions_2' => 'actionsSubtable2')),
+ array(5, array('Actions_Actions_5' => 'actionsSubtable5')),
+ array(151, array('Actions_Actions_151' => 'actionsSubtable151')),
+ array(152, array('Actions_Actions_152' => 'actionsSubtable152')),
+ array(399, array('Actions_Actions_399' => 'actionsSubtable399')),
+ // this one does not exist
+ array(404, array('Actions_Actions_404' => 0)),
+ );
+ }
+
+ private function createManyDifferentArchiveBlobs()
+ {
+ $chunk = new Chunk();
+ $chunk0 = $chunk->getBlobIdForTable(0);
+
+ $this->createArchiveBlobEntry('2013-01-01', array(
+ 'Actions_Actionsurl' => array(
+ 0 => 'test01'
+ )
+ ));
+ $this->createArchiveBlobEntry('2013-01-02', array(
+ 'Actions_Actionsurl' => array(
+ 0 => 'test02',
+ 1 => 'test1',
+ 2 => 'test2'
+ ),
+ 'Actions_Actions' => array(
+ 0 => 'actions_02',
+ $chunk0 => serialize(array(1 => 'actionsSubtable1', 2 => 'actionsSubtable2', 5 => 'actionsSubtable5'))
+ )
+ ));
+ $this->createArchiveBlobEntry('2013-01-03', array(
+ 'Actions_Actionsurl' => array(
+ 0 => 'test03',
+ $chunk0 => serialize(array(1 => 'subtable1', 2 => 'subtable2', 5 => 'subtable5'))
+ ),
+ 'Actions_Actions' => array(
+ 0 => 'actions_03',
+ 1 => 'actionsTest1',
+ 2 => 'actionsTest2')
+ )
+ );
+ $this->createArchiveBlobEntry('2013-01-04', array(
+ 'Actions_Actionsurl' => array(
+ 0 => 'test04',
+ 5 => 'subtable45',
+ 6 => 'subtable6')
+ )
+ );
+ $this->createArchiveBlobEntry('2013-01-06', array(
+ 'Actions_Actionsurl' => array(
+ 0 => 'test06',
+ $chunk0 => serialize(array()))
+ )
+ );
+ }
+
+ private function assertArchiveBlob(PiwikArchive\DataCollection $dataCollection, $date, $expectedBlob)
+ {
+ $dateIndex = $date . ',' . $date;
+ $dataArrays = $dataCollection->get(1, $dateIndex);
+
+ if (!empty($expectedBlob) && 0 !== reset($expectedBlob)) {
+ $this->assertNotEmpty($dataArrays['_metadata']['ts_archived']);
+ $dataArrays['_metadata']['ts_archived'] = true;
+ unset($dataArrays['_metadata']);
+ }
+
+ $this->assertEquals($expectedBlob, $dataArrays);
+ }
+
+ private function createArchiveBlobEntry($date, $blobs)
+ {
+ $oPeriod = PeriodFactory::makePeriodFromQueryParams('UTC', 'day', $date);
+
+ $segment = new Segment(false, array(1));
+ $params = new Parameters(new Site(1), $oPeriod, $segment);
+ $writer = new ArchiveWriter($params, false);
+ $writer->initNewArchive();
+ foreach ($blobs as $name => $blob) {
+ $writer->insertBlobRecord($name, $blob);
+ }
+ $writer->finalizeArchive();
+ }
+
private function assertArchiveTablesAreNotEmpty($tableMonth)
{
$this->assertNotEquals(0, $this->getRangeArchiveTableCount('archive_numeric', $tableMonth));
@@ -120,15 +323,20 @@ class ArchiveTest extends IntegrationTestCase
private function initiateArchivingForRange()
{
- $archive = Archive::build(self::$fixture->idSite, 'range', '2010-03-04,2010-03-07');
+ $archive = $this->getArchive('range');
return $archive->getNumeric('nb_visits');
}
private function archiveDataForIndividualDays()
{
- $archive = Archive::build(self::$fixture->idSite, 'day', '2010-03-04,2010-03-07');
+ $archive = $this->getArchive('day');
return $archive->getNumeric('nb_visits');
}
+
+ private function getArchive($period, $day = '2010-03-04,2010-03-07')
+ {
+ return Archive::build(self::$fixture->idSite, $period, $day);
+ }
}
ArchiveTest::$fixture = new OneVisitorTwoVisits(); \ No newline at end of file
diff --git a/tests/PHPUnit/Integration/DbTest.php b/tests/PHPUnit/Integration/DbTest.php
index e450ec7bae..f11cfd702e 100644
--- a/tests/PHPUnit/Integration/DbTest.php
+++ b/tests/PHPUnit/Integration/DbTest.php
@@ -29,4 +29,30 @@ class DbTest extends IntegrationTestCase
$this->assertEquals($expectedColumnNames, $colmuns);
}
+
+ /**
+ * @dataProvider getIsOptimizeInnoDBTestData
+ */
+ public function test_isOptimizeInnoDBSupported_ReturnsCorrectResult($version, $expectedResult)
+ {
+ $result = Db::isOptimizeInnoDBSupported($version);
+ $this->assertEquals($expectedResult, $result);
+ }
+
+ public function getIsOptimizeInnoDBTestData()
+ {
+ return array(
+ array("10.0.17-MariaDB-1~trusty", false),
+ array("10.1.1-MariaDB-1~trusty", true),
+ array("10.2.0-MariaDB-1~trusty", true),
+ array("10.6.19-0ubuntu0.14.04.1", false),
+
+ // for sanity. maybe not ours.
+ array("", false),
+ array(0, false),
+ array(false, false),
+ array("slkdf(@*#lkesjfMariaDB", false),
+ array("slkdfjq3rujlkv", false),
+ );
+ }
}
diff --git a/tests/PHPUnit/System/OneVisitorOneWebsiteSeveralDaysDateRangeArchivingTestsTest.php b/tests/PHPUnit/System/OneVisitorOneWebsiteSeveralDaysDateRangeArchivingTestsTest.php
index c283980c47..87e0a8294f 100755
--- a/tests/PHPUnit/System/OneVisitorOneWebsiteSeveralDaysDateRangeArchivingTestsTest.php
+++ b/tests/PHPUnit/System/OneVisitorOneWebsiteSeveralDaysDateRangeArchivingTestsTest.php
@@ -7,6 +7,7 @@
*/
namespace Piwik\Tests\System;
+use Piwik\Archive\Chunk;
use Piwik\Common;
use Piwik\Db;
use Piwik\Piwik;
@@ -105,11 +106,11 @@ class OneVisitorOneWebsiteSeveralDaysDateRangeArchivingTest extends SystemTestCa
$expectedActionsBlobs = 5;
// When flat=1, Actions plugin will process 5 + 3 extra blobs (URL = 'http://example.org/sub1/sub2/sub3/news')
- $expectedActionsBlobsWhenFlattened = $expectedActionsBlobs + 3;
+ $expectedActionsBlobsWhenFlattened = $expectedActionsBlobs + 1;
$tests = array(
// TODO Implement fix, then remove the +3 below
- 'archive_blob_2010_12' => ( ($expectedActionsBlobs+3) /*Actions*/
+ 'archive_blob_2010_12' => ( ($expectedActionsBlobs+1) /*Actions*/
+ 2 /* Resolution */
+ 2 /* VisitTime */) * 3,
@@ -163,6 +164,37 @@ class OneVisitorOneWebsiteSeveralDaysDateRangeArchivingTest extends SystemTestCa
}
/**
+ * Check that requesting period "Range" means only processing
+ * the requested Plugin blob (Actions in this case), not all Plugins blobs
+ *
+ * @depends testApi
+ */
+ public function test_checkArchiveRecords_shouldMergeSubtablesIntoOneRow()
+ {
+ $tests = array(
+ 'archive_blob_2010_12' => 3,
+
+ /**
+ * In the January date range,
+ * we archive only Actions plugins.
+ * It is flattened so all 3 sub-tables should be archived.
+ */
+ 'archive_blob_2011_01' => 3,
+ );
+ $chunk = new Chunk();
+ foreach ($tests as $table => $expectedNumSubtables) {
+ $chunkAppendix = $chunk->getBlobIdForTable(0);
+ $sql = "SELECT value FROM " . Common::prefixTable($table) . " WHERE period = " . Piwik::$idPeriods['range'] . " and `name` ='Actions_actions_url_$chunkAppendix'";
+ $blob = Db::get()->fetchOne($sql);
+ $blob = gzuncompress($blob);
+ $blob = unserialize($blob);
+ $countSubtables = count($blob);
+
+ $this->assertEquals($expectedNumSubtables, $countSubtables, "Actions_actions_url_subtables in $table expected to contain $expectedNumSubtables subtables, got $countSubtables");
+ }
+ }
+
+ /**
* @param $table
*/
protected function printDebugWhenTestFails($table)
diff --git a/tests/PHPUnit/System/TwoVisitsWithCustomVariablesSegmentMatchVisitorTypeTest.php b/tests/PHPUnit/System/TwoVisitsWithCustomVariablesSegmentMatchVisitorTypeTest.php
index 77bb4f8dac..6c75fb32d2 100755
--- a/tests/PHPUnit/System/TwoVisitsWithCustomVariablesSegmentMatchVisitorTypeTest.php
+++ b/tests/PHPUnit/System/TwoVisitsWithCustomVariablesSegmentMatchVisitorTypeTest.php
@@ -7,10 +7,12 @@
*/
namespace Piwik\Tests\System;
+use Piwik\Archive\Chunk;
use Piwik\Common;
use Piwik\Archive\ArchiveInvalidator;
use Piwik\CronArchive\SitesToReprocessDistributedList;
use Piwik\Db;
+use Piwik\Piwik;
use Piwik\Tests\Framework\TestCase\SystemTestCase;
use Piwik\Tests\Fixtures\TwoVisitsWithCustomVariables;
@@ -74,9 +76,9 @@ class TwoVisitsWithCustomVariablesSegmentMatchVisitorTypeTest extends SystemTest
// 1) CHECK 'day' archive stored in January
// We expect 2 segments
// * (1 custom variable name + 2 ref metrics
- // + 6 subtable for the custom var values + 5 Referrers blob
+ // + 1 subtable for the custom var values + 5 Referrers blob (2 of them subtables)
// )
- 'archive_blob_2010_01' => 28,
+ 'archive_blob_2010_01' => 18,
// This contains all 'last N' weeks & days,
// (2 metrics
// + 2 referrer metrics
@@ -86,8 +88,8 @@ class TwoVisitsWithCustomVariablesSegmentMatchVisitorTypeTest extends SystemTest
'archive_numeric_2010_01' => 138,
// 2) CHECK 'week' archive stored in December (week starts the month before)
- // We expect 2 segments * (1 custom variable name + 2 ref metrics + 5 subtable for the values of the name + 5 referrers blob)
- 'archive_blob_2009_12' => 28,
+ // We expect 2 segments * (1 custom variable name + 2 ref metrics + 1 subtable for the values of the name + 5 referrers blob (2 of them subtables))
+ 'archive_blob_2009_12' => 18,
// 7 metrics,
// 2 Referrer metrics (Referrers_distinctSearchEngines/Referrers_distinctKeywords),
// 6 done flag (referrers, CustomVar, VisitsSummary), 3 for period = 1 and 3 for period = 2
@@ -107,6 +109,51 @@ class TwoVisitsWithCustomVariablesSegmentMatchVisitorTypeTest extends SystemTest
}
}
+ /**
+ * Check that it merges all subtables into one blob entry
+ *
+ * @depends testApi
+ */
+ public function test_checkArchiveRecords_shouldMergeSubtablesIntoOneRow()
+ {
+ $chunk = new Chunk();
+ $chunkBlobId = $chunk->getBlobIdForTable(0);
+
+ $tests = array(
+ 'archive_blob_2010_01' => array(
+ 'CustomVariables_valueByName_' . $chunkBlobId => 6,
+ 'Referrers_keywordBySearchEngine_' . $chunkBlobId => 1,
+ 'Referrers_searchEngineByKeyword_' . $chunkBlobId => 1,
+ ),
+ 'archive_blob_2009_12' => array(
+ 'CustomVariables_valueByName_' . $chunkBlobId => 6,
+ 'Referrers_keywordBySearchEngine_' . $chunkBlobId => 1,
+ 'Referrers_searchEngineByKeyword_' . $chunkBlobId => 1,
+ )
+ );
+ $numTests = 0;
+ foreach ($tests as $table => $expectedSubtables) {
+ foreach ($expectedSubtables as $name => $expectedNumSubtables) {
+ $sql = "SELECT `value` FROM " . Common::prefixTable($table) . " WHERE `name` ='$name'";
+ $blobs = Db::get()->fetchAll($sql);
+
+ foreach ($blobs as $blob) {
+ $numTests++;
+ $blob = $blob['value'];
+ $blob = gzuncompress($blob);
+ $blob = unserialize($blob);
+
+ $countSubtables = count($blob);
+
+ $this->assertEquals($expectedNumSubtables, $countSubtables, "$name in $table expected to contain $expectedNumSubtables subtables, got $countSubtables");
+ }
+ }
+ }
+
+ // 6 _subtables entries + 6 _subtables entries for the segment
+ $this->assertEquals(12, $numTests, "$numTests were executed but expected 12");
+ }
+
public static function getOutputPrefix()
{
return 'twoVisitsWithCustomVariables_segmentMatchVisitorType';
diff --git a/tests/PHPUnit/Unit/Archive/ChunkTest.php b/tests/PHPUnit/Unit/Archive/ChunkTest.php
new file mode 100644
index 0000000000..47a0f87702
--- /dev/null
+++ b/tests/PHPUnit/Unit/Archive/ChunkTest.php
@@ -0,0 +1,148 @@
+<?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\Unit;
+
+use Piwik\Archive\Chunk;
+use Piwik\Tests\Framework\TestCase\UnitTestCase;
+
+/**
+ * @group ChunkTest
+ * @group Chunk
+ * @group Archive
+ * @group Core
+ */
+class ChunkTest extends UnitTestCase
+{
+ /**
+ * @var Chunk
+ */
+ private $chunk;
+
+ public function setUp()
+ {
+ parent::setUp();
+ $this->chunk = new Chunk();
+ }
+
+ /**
+ * @dataProvider getBlobIdForTableDataProvider
+ */
+ public function test_getBlobIdForTable_shouldSplitChunksIntoBitsOf100($expectedChunk, $tableId)
+ {
+ $this->assertEquals('chunk_' . $expectedChunk, $this->chunk->getBlobIdForTable($tableId));
+ }
+
+ public function getBlobIdForTableDataProvider()
+ {
+ return array(
+ array($expectedChunk = 0, $tableId = 0),
+ array(0, 1),
+ array(0, 45),
+ array(0, 99),
+ array(1, 100),
+ array(1, 101),
+ array(1, 134),
+ array(1, 199),
+ array(2, 200),
+ array(10, 1000),
+ array(99, 9999),
+ array(100, 10000),
+ );
+ }
+
+ /**
+ * @dataProvider isBlobIdAChunkDataProvider
+ */
+ public function test_isBlobIdAChunk($isChunk, $blobId)
+ {
+ $this->assertSame($isChunk, $this->chunk->isBlobIdAChunk($blobId));
+ }
+
+ public function isBlobIdAChunkDataProvider()
+ {
+ return array(
+ array($isChunk = true, $blobId = 'chunk_0'),
+ array(true, 'chunk_999'),
+ array(false, 'chunk0'),
+ array(false, 'chunk999'),
+ array(false, '0'),
+ array(false, '5'),
+ array(false, 5),
+ array(false, '_5'),
+ );
+ }
+
+ /**
+ * @dataProvider isRecordNameAChunkDataProvider
+ */
+ public function test_isRecordNameAChunk_shouldSplitChunksIntoBitsOf100($isChunk, $recordName)
+ {
+ $this->assertSame($isChunk, $this->chunk->isRecordNameAChunk($recordName));
+ }
+
+ public function isRecordNameAChunkDataProvider()
+ {
+ return array(
+ array($isChunk = true, $recordName = 'Actions_ActionsUrl_chunk_0'),
+ array(true, 'Actions_ActionsUrl_chunk_9999'),
+ array(true, 'Actions_ActionsUrl_chunk_4'),
+ array(false, 'Actions_ActionsUrl_chunk_ActionsTest_4'), // should end with _chunk_NUMERIC
+ array(false, 'Actions_ActionsUrl_chunk_4_ActionsTest'), // should end with _chunk_NUMERIC
+ array(false, 'Actions_ActionsUrl_chunk9999'),
+ array(false, 'Actions_ActionsUrlchunk_9999'),
+ array(false, 'chunk_9999'),
+ array(false, 'chunk_9999'),
+ );
+ }
+
+ public function test_moveArchiveBlobsIntoChunks_NoChunksGiven()
+ {
+ $this->assertSame(array(), $this->chunk->moveArchiveBlobsIntoChunks(array()));
+ }
+
+ /**
+ * @dataProvider isRecordNameAChunkDataProvider
+ */
+ public function test_moveArchiveBlobsIntoChunks_shouldSplitBlobsIntoChunks()
+ {
+ $array = array_fill(0, 245, 'test');
+ $expected = array(
+ 'chunk_0' => array_fill(0, Chunk::NUM_TABLES_IN_CHUNK, 'test'),
+ 'chunk_1' => array_fill(100, Chunk::NUM_TABLES_IN_CHUNK, 'test'),
+ 'chunk_2' => array_fill(200, 45, 'test'),
+ );
+
+ $this->assertSame($expected, $this->chunk->moveArchiveBlobsIntoChunks($array));
+ }
+
+ /**
+ * @dataProvider getRecordNameWithoutChunkAppendixDataProvider
+ */
+ public function test_getRecordNameWithoutChunkAppendix_shouldSplitChunksIntoBitsOf100($realName, $recordName)
+ {
+ $this->assertSame($realName, $this->chunk->getRecordNameWithoutChunkAppendix($recordName));
+ }
+
+ public function getRecordNameWithoutChunkAppendixDataProvider()
+ {
+ return array(
+ array($isChunk = 'Actions_ActionsUrl', $recordName = 'Actions_ActionsUrl_chunk_0'),
+ array('Actions_ActionsUrl', 'Actions_ActionsUrl_chunk_9999'),
+ array('Actions_ActionsUrl', 'Actions_ActionsUrl_chunk_4'),
+ array('Actions_ActionsUrl', 'Actions_ActionsUrl_chunk_ActionsTest_4'),
+ array('Actions_ActionsUrl', 'Actions_ActionsUrl_chunk_4_ActionsTest'),
+ // the following are not chunks so we do return the full record name
+ array('Actions_ActionsUrl_chunk9999', 'Actions_ActionsUrl_chunk9999'),
+ array('Actions_ActionsUrlchunk_9999', 'Actions_ActionsUrlchunk_9999'),
+ array('chunk_9999', 'chunk_9999'),
+ array('chunk_9999', 'chunk_9999'),
+ );
+ }
+
+} \ No newline at end of file
diff --git a/tests/PHPUnit/Unit/ArchiveProcessorTest.php b/tests/PHPUnit/Unit/ArchiveProcessorTest.php
new file mode 100644
index 0000000000..a42f60c928
--- /dev/null
+++ b/tests/PHPUnit/Unit/ArchiveProcessorTest.php
@@ -0,0 +1,129 @@
+<?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\Unit;
+
+use Piwik\Archive\Chunk;
+use Piwik\ArchiveProcessor;
+use Piwik\ArchiveProcessor\Parameters;
+use Piwik\DataTable;
+use Piwik\Segment;
+use Piwik\Tests\Framework\Mock\Site;
+use Piwik\Tests\Framework\TestCase\UnitTestCase;
+use Piwik\Period\Factory as PeriodFactory;
+
+/**
+ * @group ArchiveProcessorTest
+ * @group ArchiveProcessor
+ * @group Archive
+ * @group Core
+ */
+class ArchiveProcessorTest extends UnitTestCase
+{
+
+ public function test_insertBlobRecord_NoBlobsGiven()
+ {
+ $this->assertInsertBlobRecordPassesBlobsToArchiveWriter(array(), array());
+ }
+
+ public function test_insertBlobRecord_OnlyRootTableGiven_ShouldNotMoveRootTableIntoAChunk()
+ {
+ $blobs = array(0 => $this->getSerializedBlob());
+ $this->assertInsertBlobRecordPassesBlobsToArchiveWriter($blobs, $blobs);
+ }
+
+ public function test_insertBlobRecord_RootAndSubTablesGiven_OnlyAfewSubtables()
+ {
+ $blobs = $this->generateBlobs(0, 45);
+
+ $expectedBlobs = array(
+ 0 => $this->getSerializedBlob('_0'),
+ 'chunk_0' => serialize($this->generateBlobs(1, 44)), // does not start with zero as zero is root table
+ );
+
+ $this->assertInsertBlobRecordPassesBlobsToArchiveWriter($expectedBlobs, $blobs);
+ }
+
+ public function test_insertBlobRecord_RootAndSubTablesGiven_ShouldOnlySplitSubtablesIntoAChunk()
+ {
+ $blobs = $this->generateBlobs(0, 1145);
+
+ $expectedBlobs = array(
+ 0 => $this->getSerializedBlob('_0'),
+ 'chunk_0' => serialize($this->generateBlobs(1, Chunk::NUM_TABLES_IN_CHUNK - 1)), // does not start with zero as zero is root table
+ 'chunk_1' => serialize($this->generateBlobs(100, Chunk::NUM_TABLES_IN_CHUNK)),
+ 'chunk_2' => serialize($this->generateBlobs(200, Chunk::NUM_TABLES_IN_CHUNK)),
+ 'chunk_3' => serialize($this->generateBlobs(300, Chunk::NUM_TABLES_IN_CHUNK)),
+ 'chunk_4' => serialize($this->generateBlobs(400, Chunk::NUM_TABLES_IN_CHUNK)),
+ 'chunk_5' => serialize($this->generateBlobs(500, Chunk::NUM_TABLES_IN_CHUNK)),
+ 'chunk_6' => serialize($this->generateBlobs(600, Chunk::NUM_TABLES_IN_CHUNK)),
+ 'chunk_7' => serialize($this->generateBlobs(700, Chunk::NUM_TABLES_IN_CHUNK)),
+ 'chunk_8' => serialize($this->generateBlobs(800, Chunk::NUM_TABLES_IN_CHUNK)),
+ 'chunk_9' => serialize($this->generateBlobs(900, Chunk::NUM_TABLES_IN_CHUNK)),
+ 'chunk_10' => serialize($this->generateBlobs(1000, Chunk::NUM_TABLES_IN_CHUNK)),
+ 'chunk_11' => serialize($this->generateBlobs(1100, 45)),
+ );
+
+ $this->assertInsertBlobRecordPassesBlobsToArchiveWriter($expectedBlobs, $blobs);
+ }
+
+ public function test_insertBlobRecord_ShouldBeAbleToHandleAString()
+ {
+ $serialized = $this->getSerializedBlob();
+
+ $this->assertInsertBlobRecordPassesBlobsToArchiveWriter($serialized, $serialized);
+ }
+
+ private function generateBlobs($startIndex, $numberOfEntries)
+ {
+ $blobs = array();
+
+ for ($i = 0; $i < $numberOfEntries; $i++) {
+ $subtableId = $startIndex + $i;
+ // we need to append something to make sure it actually moves the correct blob into the correct chunk
+ $blobs[$subtableId] = $this->getSerializedBlob('_'. $subtableId);
+ }
+
+ return $blobs;
+ }
+
+ private function getSerializedBlob($appendix = '')
+ {
+ return 'a:1:{i:0;a:3:{i:0;a:0:{}i:1;a:0:{}i:3;N;}}' . $appendix;
+ }
+
+ private function assertInsertBlobRecordPassesBlobsToArchiveWriter($expectedBlobs, $blobs)
+ {
+ $recordName = 'Actions_Action_url';
+
+ $writer = $this->getMock('Piwik\DataAccess\ArchiveWriter', array('insertBlobRecord'), array(), '', false);
+ $writer->expects($this->once())
+ ->method('insertBlobRecord')
+ ->with($recordName, $expectedBlobs);
+
+ $processor = $this->createProcessor($writer);
+ $processor->insertBlobRecord($recordName, $blobs);
+ }
+
+ private function createArchiveProcessorParamaters()
+ {
+ $oPeriod = PeriodFactory::makePeriodFromQueryParams('UTC', 'day', '2015-01-01');
+
+ $segment = new Segment(false, array(1));
+ $params = new Parameters(new Site(1), $oPeriod, $segment);
+
+ return $params;
+ }
+
+ private function createProcessor($writer)
+ {
+ $params = $this->createArchiveProcessorParamaters();
+
+ return new ArchiveProcessor($params, $writer);
+ }
+} \ No newline at end of file
diff --git a/tests/PHPUnit/Unit/DataAccess/ArchiveWriterTest.php b/tests/PHPUnit/Unit/DataAccess/ArchiveWriterTest.php
new file mode 100644
index 0000000000..1019fe91c6
--- /dev/null
+++ b/tests/PHPUnit/Unit/DataAccess/ArchiveWriterTest.php
@@ -0,0 +1,110 @@
+<?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\Unit;
+
+use Piwik\DataAccess\ArchiveWriter;
+use Piwik\DataTable;
+use Piwik\Segment;
+use Piwik\Tests\Framework\Mock\Site;
+use Piwik\Tests\Framework\TestCase\UnitTestCase;
+use Piwik\Period\Factory as PeriodFactory;
+
+/**
+ * @group ArchiveWriterTest
+ * @group ArchiveWriter
+ * @group Archive
+ * @group Core
+ */
+class ArchiveWriterTest extends UnitTestCase
+{
+ private $recordName = 'Actions_Action_url';
+
+ public function test_insertBlobRecord_NoBlobsGiven_ShouldInsertNothing()
+ {
+ $this->assertInsertBlobRecordInsertedRecordsInBulk(array(), array());
+ }
+
+ public function test_insertBlobRecord_ShouldAppendTheRecordNameToSubtables()
+ {
+ $blobs = array(
+ 0 => $this->getSerializedBlob('_root'),
+ 1 => $this->getSerializedBlob('subtable1'),
+ 4 => $this->getSerializedBlob('subtable4'),
+ 5 => $this->getSerializedBlob('subtable5')
+ );
+
+ $expectedBlobs = array(
+ array($this->recordName , $this->getSerializedBlob('_root')),
+ array($this->recordName . '_1', $this->getSerializedBlob('subtable1')),
+ array($this->recordName . '_4', $this->getSerializedBlob('subtable4')),
+ array($this->recordName . '_5', $this->getSerializedBlob('subtable5'))
+ );
+
+ $this->assertInsertBlobRecordInsertedRecordsInBulk($expectedBlobs, $blobs);
+ }
+
+ public function test_insertBlobRecord_ShouldAppendTheRecordNameToChunks()
+ {
+ $blobs = array(
+ 0 => $this->getSerializedBlob('_root'),
+ 'chunk_0' => $this->getSerializedBlob('chunk0'),
+ 'chunk_1' => $this->getSerializedBlob('chunk1'),
+ 'chunk_2' => $this->getSerializedBlob('chunk2')
+ );
+
+ $expectedBlobs = array(
+ array($this->recordName , $this->getSerializedBlob('_root')),
+ array($this->recordName . '_chunk_0', $this->getSerializedBlob('chunk0')),
+ array($this->recordName . '_chunk_1', $this->getSerializedBlob('chunk1')),
+ array($this->recordName . '_chunk_2', $this->getSerializedBlob('chunk2'))
+ );
+
+ $this->assertInsertBlobRecordInsertedRecordsInBulk($expectedBlobs, $blobs);
+ }
+
+ public function test_insertBlobRecord_ShouldInsertASingleRecord_IfNotAnArrayOfBlobsIsGiven()
+ {
+ $blob = $this->getSerializedBlob('_root');
+
+ $this->assertInsertBlobRecordInsertedASingleRecord($blob, $blob);
+ }
+
+ private function getSerializedBlob($appendix = '')
+ {
+ return 'a:1:{i:0;a:3:{i:0;a:0:{}i:1;a:0:{}i:3;N;}}' . $appendix;
+ }
+
+ private function assertInsertBlobRecordInsertedRecordsInBulk($expectedBlobs, $blobs)
+ {
+ $writer = $this->getMock('Piwik\DataAccess\ArchiveWriter', array('insertBulkRecords', 'compress'), array(), '', false);
+ $writer->expects($this->exactly(count($blobs)))
+ ->method('compress')
+ ->will($this->returnArgument(0));
+ $writer->expects($this->once())
+ ->method('insertBulkRecords')
+ ->with($expectedBlobs);
+
+ /** @var ArchiveWriter $writer */
+ $writer->insertBlobRecord($this->recordName, $blobs);
+ }
+
+ private function assertInsertBlobRecordInsertedASingleRecord($expectedBlob, $blob)
+ {
+ $writer = $this->getMock('Piwik\DataAccess\ArchiveWriter', array('insertRecord', 'compress'), array(), '', false);
+ $writer->expects($this->once())
+ ->method('compress')
+ ->will($this->returnArgument(0));
+ $writer->expects($this->once())
+ ->method('insertRecord')
+ ->with($this->recordName, $expectedBlob);
+
+ /** @var ArchiveWriter $writer */
+ $writer->insertBlobRecord($this->recordName, $blob);
+ }
+} \ No newline at end of file
diff --git a/tests/PHPUnit/Unit/DataTable/Filter/TruncateTest.php b/tests/PHPUnit/Unit/DataTable/Filter/TruncateTest.php
index b3b5710f91..c0c8e1465b 100644
--- a/tests/PHPUnit/Unit/DataTable/Filter/TruncateTest.php
+++ b/tests/PHPUnit/Unit/DataTable/Filter/TruncateTest.php
@@ -33,7 +33,7 @@ class DataTable_Filter_TruncateTest extends \PHPUnit_Framework_TestCase
// from the database is in conflict with one of the Manager managed table identifiers.
// This is a rare but legitimate case as identifiers are not thoroughly synchronized
// when the expanded parameter is false.
- $rowBeingFiltered->c[Row::DATATABLE_ASSOCIATED] = $mockedDataTable->getId();
+ $rowBeingFiltered->subtableId = $mockedDataTable->getId();
$filter = new Truncate($dataTableBeingFiltered, 1);
$filter->filter($dataTableBeingFiltered);
@@ -55,7 +55,7 @@ class DataTable_Filter_TruncateTest extends \PHPUnit_Framework_TestCase
// enclosing DataTable.
// This can happen because identifiers are not thoroughly synchronized when the expanded parameter
// is false.
- $rowBeingFiltered->c[Row::DATATABLE_ASSOCIATED] = $dataTableBeingFiltered->getId();
+ $rowBeingFiltered->subtableId = $dataTableBeingFiltered->getId();
$filter = new Truncate($dataTableBeingFiltered, 1);
$filter->filter($dataTableBeingFiltered);
diff --git a/tests/PHPUnit/Unit/DataTable/ManagerTest.php b/tests/PHPUnit/Unit/DataTable/ManagerTest.php
new file mode 100644
index 0000000000..60c1f54d67
--- /dev/null
+++ b/tests/PHPUnit/Unit/DataTable/ManagerTest.php
@@ -0,0 +1,244 @@
+<?php
+
+namespace Piwik\Tests\Unit\DataTable;
+
+use Piwik\Config;
+use Piwik\DataTable\Manager;
+use Piwik\DataTable;
+use Piwik\DataTable\Row;
+use Piwik\Tests\Framework\TestCase\UnitTestCase;
+
+/**
+ * @group DataTable
+ * @group ManagerTest
+ * @group Core
+ */
+class ManagerTest extends UnitTestCase
+{
+ /**
+ * @var Manager
+ */
+ private $manager;
+
+ public function setUp()
+ {
+ parent::setUp();
+ $this->manager = new Manager();
+ }
+
+ private function createTestDataTable()
+ {
+ return new DataTable();
+ }
+
+ /**
+ * @expectedException \Piwik\DataTable\TableNotFoundException
+ * @expectedExceptionMessage table id 1 not found in memory
+ */
+ public function test_getTable_shouldThrowException_IfTableIdDoesNotExist()
+ {
+ $this->manager->getTable(1);
+ }
+
+ public function test_getTable_shouldFindAnExistingTable_IfTableExists()
+ {
+ $table1 = $this->createTestDataTable();
+ $this->manager->addTable($table1);
+
+ $table2 = $this->manager->getTable($this->manager->getMostRecentTableId());
+
+ $this->assertSame($table1, $table2);
+ }
+
+ public function test_addTable_shouldIncreaseTheTableId()
+ {
+ $table = $this->createTestDataTable();
+ $id = $this->manager->addTable($table);
+
+ $this->assertSame(1, $id);
+
+ // another one
+ $id = $this->manager->addTable($table);
+
+ $this->assertSame(2, $id);
+ }
+
+ public function test_getMostRecentTableId_shouldAlwaysReturnTheMostRecentlyCreatedId()
+ {
+ $this->assertSame(0, $this->manager->getMostRecentTableId());
+
+ $this->addDataTables(1);
+ $this->assertSame(1, $this->manager->getMostRecentTableId());
+
+ // another one
+ $this->addDataTables(1);
+ $this->assertSame(2, $this->manager->getMostRecentTableId());
+
+ $this->addDataTables(3);
+ $this->assertSame(5, $this->manager->getMostRecentTableId());
+ }
+
+ public function test_setTableDeleted_shouldActuallyUnsetTheTable()
+ {
+ $this->addDataTables(1);
+
+ $this->manager->setTableDeleted($id = 1);
+
+ $this->assertDataTablesInManager(array(1 => null));
+ }
+
+ public function test_setTableDeleted_shouldOnlyUnsetOneTableHavingTheGivenId()
+ {
+ $tables = $this->addDataTables(3);
+
+ $this->manager->setTableDeleted($id = 2);
+
+ $this->assertDataTablesInManager(array(1 => $tables[1], 2 => null, 3 => $tables[3]));
+ }
+
+ public function test_deleteTable_shouldNotDeleteAnything_IfTableDoesNotExist()
+ {
+ $tables = $this->addDataTables(1);
+
+ $this->manager->deleteTable($id = 0);
+ $this->manager->deleteTable($id = 99);
+ $this->manager->deleteTable($id = 5);
+
+ $this->assertDataTablesInManager($tables);
+ }
+
+ public function test_deleteTable_shouldSetTheGivenDataTableDeleted_IfIdExists()
+ {
+ $tables = $this->addDataTables(3);
+
+ $this->manager->deleteTable($id = 2);
+
+ $this->assertDataTablesInManager(array(1 => $tables[1], 2 => null, 3 => $tables[3]));
+ }
+
+ public function test_deleteAll_shouldActuallyRemoveAllTables()
+ {
+ $this->addDataTables(3);
+
+ $this->manager->deleteAll();
+
+ $this->assertDataTablesInManager(array());
+ }
+
+ public function test_deleteAll_shouldRemoveAllTables_HigherThanTheGivenId()
+ {
+ $tables = $this->addDataTables(4);
+
+ $this->manager->deleteAll($id = 2);
+
+ $this->assertDataTablesInManager(array(1 => $tables[1], 2 => $tables[2], 3 => null, 4 => null));
+ }
+
+ public function test_deleteAll_shouldNotRemoveAnything_IfIdIsTooHighToMatchAny()
+ {
+ $tables = $this->addDataTables(4);
+
+ $this->manager->deleteAll($id = 99);
+
+ $this->assertDataTablesInManager($tables);
+ }
+
+ public function test_deleteAll_shouldNotAlterTheNextDataTableId_IfAnIdIsGiven()
+ {
+ $this->addDataTables(4);
+
+ $this->manager->deleteAll($id = 2);
+
+ $this->assertMostRecentDataTableId(4);
+ }
+
+ public function test_deleteAll_shouldResetNextDataTableId_IfAllTablesAreDeleted()
+ {
+ $this->addDataTables(4);
+ $this->assertMostRecentDataTableId(4); // verify it is not yet 0
+ $this->manager->deleteAll();
+ $this->assertMostRecentDataTableId(0);
+
+ $this->addDataTables(4);
+ $this->assertMostRecentDataTableId(4); // verify it is not yet 0
+ $this->manager->deleteAll($id = 0);
+ $this->assertMostRecentDataTableId(0);
+ }
+
+ public function test_deleteTablesExceptIgnored_shouldRemoveAllTables_ButIgnoreTheGivenOnes()
+ {
+ $tables = $this->addDataTables(8);
+
+ $this->manager->deleteTablesExceptIgnored(array(4, 6), $id = 2);
+
+ $this->assertDataTablesInManager(array(
+ 1 => $tables[1],
+ 2 => null,
+ 3 => null,
+ 4 => $tables[4], // supposed to be deleted but ignored
+ 5 => null,
+ 6 => $tables[6], // ignored as well
+ 7 => null,
+ 8 => null
+ ));
+ }
+
+ public function test_deleteTablesExceptIgnored_shouldRemoveAllTables_IfNoIgnoredAreGiven()
+ {
+ $tables = $this->addDataTables(5);
+
+ $this->manager->deleteTablesExceptIgnored(array(), $id = 2);
+
+ $this->assertDataTablesInManager(array(
+ 1 => $tables[1],
+ 2 => null,
+ 3 => null,
+ 4 => null,
+ 5 => null,
+ ));
+ }
+
+ public function test_deleteTablesExceptIgnored_shouldNotResetMostRecentDataTableId_EvenWhenDeletingAll()
+ {
+ $this->addDataTables(5);
+
+ $this->manager->deleteTablesExceptIgnored(array(), $id = 0);
+ $this->assertDataTablesInManager(array(
+ 1 => null,
+ 2 => null,
+ 3 => null,
+ 4 => null,
+ 5 => null,
+ ));
+
+ $this->assertMostRecentDataTableId(5);
+ }
+
+ private function assertMostRecentDataTableId($id)
+ {
+ $this->assertSame($id, $this->manager->getMostRecentTableId());
+ }
+
+ private function assertDataTablesInManager($expectedDataTables)
+ {
+ $this->assertSame($expectedDataTables, $this->manager->getArrayCopy());
+ }
+
+ /**
+ * @param $numTables
+ * @return DataTable[]
+ */
+ private function addDataTables($numTables)
+ {
+ $table = $this->createTestDataTable();
+ $tables = array();
+
+ for ($i = 0; $i<$numTables; $i++) {
+ $id = $this->manager->addTable($table);
+ $tables[$id] = $table;
+ }
+
+ return $tables;
+ }
+
+}
diff --git a/tests/PHPUnit/Unit/DataTable/RowTest.php b/tests/PHPUnit/Unit/DataTable/RowTest.php
index ae8bf699ef..be15be9d65 100644
--- a/tests/PHPUnit/Unit/DataTable/RowTest.php
+++ b/tests/PHPUnit/Unit/DataTable/RowTest.php
@@ -26,47 +26,106 @@ class RowTest extends \PHPUnit_Framework_TestCase
$this->row = new Row();
}
- public function testDataTableAssociatedIsNegativeWhenSubDataTableInMemory()
+ public function test_isSubtableLoaded_ReturnsTrue_IfDataTableAssociatedIsLoaded()
{
$testRow = $this->getTestRowWithSubDataTableLoaded();
- $this->assertTrue($testRow->c[Row::DATATABLE_ASSOCIATED] < 0);
+ $this->assertTrue($testRow->isSubtableLoaded());
+ $this->assertGreaterThanOrEqual(1, $testRow->getIdSubDataTable());
}
- public function testDataTableAssociatedIsNegativeWhenSubDataTableSetted()
+ public function test_isSubtableLoaded_ReturnsTrue_WhenSubDataTableSetted()
{
$testRow = $this->getTestRowWithSubDataTableNotLoaded();
+ $this->assertFalse($testRow->isSubtableLoaded()); // verify not already loaded
+ $this->assertEquals(50, $testRow->getIdSubDataTable());
+
$testRow->setSubtable($this->getTestSubDataTable());
- $this->assertTrue($testRow->c[Row::DATATABLE_ASSOCIATED] < 0);
+ $this->assertTrue($testRow->isSubtableLoaded());
+ $this->assertGreaterThanOrEqual(1, $testRow->getIdSubDataTable());
+ }
+
+ public function test_getIdSubDataTable_ShouldBeNullIfNoSubtableIsSet()
+ {
+ $testRow = $this->getTestRowWithNoSubDataTable();
+ $this->assertEquals(null, $testRow->getIdSubDataTable());
}
- public function testIdSubDataTabledIsPositiveWhenSubDataTableInMemory()
+ public function test_removeSubtable_ShouldRemoveASetSubtable()
{
$testRow = $this->getTestRowWithSubDataTableLoaded();
- $this->assertTrue($testRow->getIdSubDataTable() > 0);
+ $this->assertTrue($testRow->isSubtableLoaded());
+
+ $testRow->removeSubtable();
+
+ $this->assertFalse($testRow->isSubtableLoaded());
+ $this->assertEquals(null, $testRow->getIdSubDataTable());
}
- public function testDataTableAssociatedIsPositiveOnSerializedRow()
+ public function test_desctruct_ShouldRemoveASetSubtable()
{
$testRow = $this->getTestRowWithSubDataTableLoaded();
+ $this->assertTrue($testRow->isSubtableLoaded());
- // testDataTableAssociatedIsPositiveOnSerializedRow is only valid as long as the Row is not modified after being unserialized
- $this->assertFalse(method_exists($testRow, '__wakeup'));
+ $testRow->__destruct();
- $serializedTestRow = serialize($testRow);
- $unserializedTestRow = unserialize($serializedTestRow);
+ $this->assertFalse($testRow->isSubtableLoaded());
+ $this->assertEquals(null, $testRow->getIdSubDataTable());
+ }
+
+ public function test_canBeCloned_ShouldRemoveASetSubtable()
+ {
+ $testRow = $this->getTestRowWithNoSubDataTable();
+ $testRow->setColumn('label', 'test');
- $this->assertTrue($unserializedTestRow->c[Row::DATATABLE_ASSOCIATED] > 0);
+ $testRow2 = clone $testRow;
+
+ $this->assertNotSame($testRow2, $testRow);
+ $this->assertEquals('test', $testRow2->getColumn('label'));
+ $this->assertEquals('test', $testRow->getColumn('label'));
+
+ $testRow->setColumn('label', 'different');
+
+ // only row 2 changes
+ $this->assertEquals('test', $testRow2->getColumn('label'));
+ $this->assertEquals('different', $testRow->getColumn('label'));
}
- public function testDataTableAssociatedIsNegativeAfterSerialize()
+ public function test_export_shouldExportColumnsMetadataAndSubtableId()
{
+ $columns = array('label' => 'test', 'nb_visits' => 5);
+
$testRow = $this->getTestRowWithSubDataTableLoaded();
+ $testRow->setColumns($columns);
+ $testRow->setMetadata('test1', 'val1');
+ $testRow->setMetadata('url', 'http://piwik.org');
+ $export = $testRow->export();
- serialize($testRow);
+ $expected = array(
+ Row::COLUMNS => $columns,
+ Row::METADATA => array('test1' => 'val1', 'url' => 'http://piwik.org')
+ );
- $testRow->cleanPostSerialize();
+ // we cannot really test for exact match since the subtableId might change when other tests are changed
+ $this->assertGreaterThan(1, $export[Row::DATATABLE_ASSOCIATED]);
+ unset($export[Row::DATATABLE_ASSOCIATED]);
- $this->assertTrue($testRow->c[Row::DATATABLE_ASSOCIATED] < 0);
+ $this->assertSame($expected, $export);
+ }
+
+ public function test_isSubtableLoaded_ShouldReturnFalse_WhenRestoringAnExportedRow()
+ {
+ $testRow = $this->getTestRowWithSubDataTableLoaded();
+
+ // serialize and unserialize is not needed for this test case, the export is the important part.
+ // we still do it, to have it more "realistic"
+ $serializedTestRow = serialize($testRow->export());
+ $unserializedTestRow = unserialize($serializedTestRow);
+
+ /** @var Row $unserializedTestRow */
+ $row = new Row($unserializedTestRow);
+
+ $this->assertTrue($row->getIdSubDataTable() > 0);
+ $this->assertFalse($row->isSubtableLoaded());
}
public function testIsSubDataTableLoadedIsTrueWhenSubDataTableInMemory()
@@ -81,6 +140,67 @@ class RowTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($testRow->isSubtableLoaded());
}
+ public function test_getMetadata_setMetadata_shouldReturnRawScalarValue()
+ {
+ $this->assertMetadataSavesValue(5, 'testInteger', 5);
+ $this->assertMetadataSavesValue(5.444, 'testFloat', 5.444);
+ $this->assertMetadataSavesValue('MyString', 'testString', 'MyString');
+ $this->assertMetadataSavesValue(array(array(1 => '5')), 'testArray', array(array(1 => '5')));
+ }
+
+ public function test_getMetadata_shouldReturnFalse_IfMetadataKeyDoesNotExists()
+ {
+ $this->assertFalse($this->row->getMetadata('anyKey_thatDoesNotExist'));
+ }
+
+ public function test_getMetadata_shouldReturnEmptyArray_IfNoParticularOneIsRequestedAndNoneAreSet()
+ {
+ $this->assertEquals(array(), $this->row->getMetadata());
+ }
+
+ public function test_getMetadata_shouldReturnAllMetadataValues_IfNoParticularOneIsRequested()
+ {
+ $this->row->setMetadata('url', 'http://piwik.org');
+ $this->row->setMetadata('segmentValue', 'test==piwik');
+
+ $this->assertEquals(array(
+ 'url' => 'http://piwik.org',
+ 'segmentValue' => 'test==piwik'
+ ), $this->row->getMetadata());
+ }
+
+ public function test_deleteMetadata_shouldReturnDeleteAllValues_WhenNoSpecificOneIsRequestedToBeDeleted()
+ {
+ $this->row->setMetadata('url', 'http://piwik.org');
+ $this->row->setMetadata('segmentValue', 'test==piwik');
+
+ $this->assertNotEmpty($this->row->getMetadata()); // make sure it is actually set
+
+ $this->row->deleteMetadata();
+
+ $this->assertSame(array(), $this->row->getMetadata());
+ }
+
+ public function test_deleteMetadata_shouldOnlyDeleteARequestedMetadataEntry_WhileKeepingOthersUntouched()
+ {
+ $this->row->setMetadata('url', 'http://piwik.org');
+ $this->row->setMetadata('segmentValue', 'test==piwik');
+
+ $this->assertTrue($this->row->deleteMetadata('url'));
+
+ $this->assertFalse($this->row->getMetadata('url'));
+ $this->assertEquals('test==piwik', $this->row->getMetadata('segmentValue'));
+ }
+
+ public function test_deleteMetadata_shouldReturnFalseAndKeepOtherEntriesUntouched_IfMetadataNameDidNotExist()
+ {
+ $this->row->setMetadata('segmentValue', 'test==piwik');
+
+ $this->assertFalse($this->row->deleteMetadata('url'));
+
+ $this->assertEquals('test==piwik', $this->row->getMetadata('segmentValue'));
+ }
+
public function test_getColumn_shouldReturnRawScalarValue()
{
$this->assertColumnSavesValue(5, 'testInteger', 5);
@@ -105,7 +225,7 @@ class RowTest extends \PHPUnit_Framework_TestCase
$this->assertColumnSavesValue(array(null, 'phpinfo'), 'testScalar', array(null, 'phpinfo'));
}
- public function test_getColumns_shouldReturnAllColumns()
+ public function test_getColumns_setColumns_shouldReturnAllColumns()
{
$this->row->setColumns(array(
'nb_visits' => 4,
@@ -120,21 +240,126 @@ class RowTest extends \PHPUnit_Framework_TestCase
);
$this->assertEquals($expected, $this->row->getColumns());
+ $this->assertEquals('Test', $this->row->getColumn('label'));
+ $this->assertEquals(4, $this->row->getColumn('nb_visits'));
+ }
+
+ public function test_deleteColumn_shouldOnlyDeleteARequestedColumnEntry_WhileKeepingOthersUntouched()
+ {
+ $this->row->setColumn('label', 'http://piwik.org');
+ $this->row->setColumn('nb_visits', '1');
+
+ $this->assertTrue($this->row->deleteColumn('nb_visits'));
+ $this->assertFalse($this->row->hasColumn('nb_visits')); // verify
+ $this->assertFalse($this->row->getMetadata('nb_visits')); // verify
+
+ $this->assertEquals('http://piwik.org', $this->row->getColumn('label')); // make sure not deleted
+ }
+
+ public function test_deleteColumn_shouldReturnFalseAndKeepOtherEntriesUntouched_IfColumnNameDidNotExist()
+ {
+ $this->row->setColumn('label', 'http://piwik.org');
+
+ $this->assertFalse($this->row->deleteColumn('nb_visits'));
+ $this->assertFalse($this->row->hasColumn('nb_visits'));
+
+ $this->assertEquals('http://piwik.org', $this->row->getColumn('label'));
+ }
+
+ public function test_deleteColumn_shouldReturnAColumnValueThatIsNull()
+ {
+ $this->row->setColumn('label', null);
+
+ $this->assertTrue($this->row->hasColumn('label'));
+ $this->assertTrue($this->row->deleteColumn('label'));
+ $this->assertFalse($this->row->hasColumn('label'));
+ }
+
+ public function test_renameColumn_shouldReturnAColumnOnly_IfAValueIsSetForThatColumn()
+ {
+ $this->row->setColumn('nb_visits', 10);
+
+ $this->row->renameColumn('nb_visits', 'nb_hits');
+
+ $this->assertFalse($this->row->hasColumn('nb_visits'));
+ $this->assertTrue($this->row->hasColumn('nb_hits'));
+ $this->assertEquals(10, $this->row->getColumn('nb_hits'));
+ }
+
+ public function test_renameColumn_shouldNotReturnAColumn_IfValueIsNotSetButRemoveColumn()
+ {
+ $this->row->setColumn('nb_visits', null);
+
+ $this->row->renameColumn('nb_visits', 'nb_hits');
+
+ $this->assertFalse($this->row->hasColumn('nb_visits'));
+ $this->assertFalse($this->row->hasColumn('nb_hits'));
+ }
+
+ public function test_renameColumn_shouldDoNothing_IfGivenColumnDoesNotExist()
+ {
+ $this->row->setColumn('nb_visits', 11);
+
+ $this->row->renameColumn('nb_hits', 'nb_pageviews');
+
+ $this->assertFalse($this->row->hasColumn('nb_hits'));
+ $this->assertFalse($this->row->hasColumn('nb_pageviews'));
+ $this->assertEquals(11, $this->row->getColumn('nb_visits'));
+ }
+
+ public function test_getSubtable_shouldReturnSubtable_IfLoaded()
+ {
+ $testRow = $this->getTestRowWithSubDataTableNotLoaded();
+ $subTable = $this->getTestSubDataTable();
+ $testRow->setSubtable($subTable);
+
+ $this->assertSame($subTable, $testRow->getSubtable());
+ }
+
+ public function test_getSubtable_shouldReturnFalse_IfSubtableExistsButIsNotLoaded()
+ {
+ $testRow = $this->getTestRowWithSubDataTableNotLoaded();
+
+ $this->assertFalse($testRow->getSubtable());
+ }
+
+ public function test_getSubtable_shouldReturnFalse_IfHasNoSubtableAtAll()
+ {
+ $testRow = $this->getTestRowWithNoSubDataTable();
+
+ $this->assertFalse($testRow->getSubtable());
}
public function test_sumSubTable_whenSubTableAlreadyExists_overwriteExistingSubtable()
{
$testRow = $this->getTestRowWithSubDataTableNotLoaded();
- $this->assertFalse( $testRow->isSubtableLoaded() );
+ $this->assertFalse($testRow->isSubtableLoaded());
$subTable = $this->getTestSubDataTable();
$testRow->setSubtable($subTable);
- $testRow->switchFlagSubtableIsLoaded();
- $this->assertFalse( $testRow->isSubtableLoaded() );
+ $this->assertTrue($testRow->isSubtableLoaded());
$testRow->sumSubtable($subTable);
- $this->assertTrue( DataTable::isEqual($testRow->getSubtable(), $subTable));
+ $this->assertTrue(DataTable::isEqual($testRow->getSubtable(), $subTable));
+ }
+
+ public function test_hasColumn()
+ {
+ $this->row->setColumns(array('test1' => 'yes', 'test2' => false, 'test3' => 5, 'test4' => array()));
+
+ $this->assertFalse($this->row->hasColumn('test')); // does not exist
+ $this->assertTrue($this->row->hasColumn('test1'));
+ $this->assertTrue($this->row->hasColumn('test2')); // even if value is false it still exists
+ $this->assertTrue($this->row->hasColumn('test3'));
+ $this->assertTrue($this->row->hasColumn('test4'));
+ }
+
+ public function test_hasColumn_shouldReturnTrueEvenIfColumnValueIsNull()
+ {
+ $this->assertFalse($this->row->hasColumn('test'));
+ $this->row->setColumn('test', null);
+ $this->assertTrue($this->row->hasColumn('test'));
}
private function assertColumnSavesValue($expectedValue, $columnName, $valueToSet)
@@ -143,19 +368,28 @@ class RowTest extends \PHPUnit_Framework_TestCase
$this->assertSame($expectedValue, $this->row->getColumn($columnName));
}
+ private function assertMetadataSavesValue($expectedValue, $metadataName, $valueToSet)
+ {
+ $this->row->setMetadata($metadataName, $valueToSet);
+ $this->assertSame($expectedValue, $this->row->getMetadata($metadataName));
+ }
+
protected function getTestRowWithSubDataTableLoaded()
{
$testSubDataTable = $this->getTestSubDataTable();
- $testRow = new Row(
- array(
- Row::DATATABLE_ASSOCIATED => $testSubDataTable
- )
- );
+ $testRow = new Row(array(
+ Row::DATATABLE_ASSOCIATED => $testSubDataTable
+ ));
return $testRow;
}
+ protected function getTestRowWithNoSubDataTable()
+ {
+ return new Row(array());
+ }
+
protected function getTestSubDataTable()
{
return new DataTable();
@@ -163,11 +397,9 @@ class RowTest extends \PHPUnit_Framework_TestCase
protected function getTestRowWithSubDataTableNotLoaded()
{
- $testRow = new Row(
- array(
- Row::DATATABLE_ASSOCIATED => 50
- )
- );
+ $testRow = new Row(array(
+ Row::DATATABLE_ASSOCIATED => 50
+ ));
return $testRow;
}
diff --git a/tests/PHPUnit/Unit/DataTableTest.php b/tests/PHPUnit/Unit/DataTableTest.php
index b36dbd5146..c03dfab4af 100644
--- a/tests/PHPUnit/Unit/DataTableTest.php
+++ b/tests/PHPUnit/Unit/DataTableTest.php
@@ -13,15 +13,15 @@ use Piwik\DataTable\Manager;
use Piwik\DataTable\Row;
use Piwik\DataTable;
use Piwik\Timer;
+use Symfony\Component\VarDumper\Cloner\Data;
/**
* @group DataTableTest
+ * @group DataTable
+ * @group Core
*/
class DataTableTest extends \PHPUnit_Framework_TestCase
{
- /**
- * @group Core
- */
public function testApplyFilter()
{
$table = $this->_getDataTable1ForTest();
@@ -46,9 +46,6 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
return $table;
}
- /**
- * @group Core
- */
public function testRenameColumn()
{
$table = $this->_getSimpleTestDataTable();
@@ -58,9 +55,6 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(10, 90, 100, 200), $table->getColumn('renamed'));
}
- /**
- * @group Core
- */
public function testDeleteColumn()
{
$table = $this->_getSimpleTestDataTable();
@@ -69,9 +63,6 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
$this->assertEquals(array(false, false, false, false), $table->getColumn('count'));
}
- /**
- * @group Core
- */
public function testDeleteRow()
{
$table = $this->_getSimpleTestDataTable();
@@ -89,9 +80,6 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
$this->assertFalse($table->getRowFromId($idToDelete));
}
- /**
- * @group Core
- */
public function testGetLastRow()
{
$table = $this->_getSimpleTestDataTable();
@@ -103,9 +91,6 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($table->getLastRow(), $table->getRowFromId($rowsCount - 2));
}
- /**
- * @group Core
- */
public function testGetRowFromIdSubDataTable()
{
$table1 = $this->_getDataTable1ForTest();
@@ -122,11 +107,22 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($table2->getRowFromIdSubDataTable($idTable3), $table2->getLastRow());
}
+ public function test_clone_shouldIncreasesTableId()
+ {
+ $table = new DataTable;
+ $rows = array(
+ array(Row::COLUMNS => array('label' => 'google')),
+ );
+ $table->addRowsFromArray($rows);
+
+ $table2 = clone $table;
+
+ $this->assertSame($table2->getId(), $table->getId() + 1);
+ }
+
/**
* we test the count rows and the count rows recursive version
* on a Simple array (1 level only)
- *
- * @group Core
*/
public function testCountRowsSimple()
{
@@ -140,12 +136,13 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
array($idcol => array('label' => 'yahoo')),
array($idcol => array('label' => 'amazon')),
array($idcol => array('label' => '238975247578949')),
- array($idcol => array('label' => 'Q*(%&*("$&%*(&"$*")"))')));
+ array($idcol => array('label' => 'Q*(%&*("$&%*(&"$*")"))'))
+ );
$table->addRowsFromArray($rows);
- $this->assertEquals(count($rows), $table->getRowsCount());
- $this->assertEquals(count($rows), $table->getRowsCountRecursive());
+ $this->assertEquals(7, $table->getRowsCount());
+ $this->assertEquals(7, $table->getRowsCountRecursive());
}
/**
@@ -155,12 +152,9 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
* the recursive count returns
* the sum of the number of rows of all the subtables
* + the number of rows in the parent table
- *
- * @group Core
*/
public function testCountRowsComplex()
{
-
$idcol = Row::COLUMNS;
$idsubtable = Row::DATATABLE_ASSOCIATED;
@@ -208,20 +202,17 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
);
$table->addRowsFromArray($rows);
- $this->assertEquals(count($rows), $table->getRowsCount());
- $countAllRows = count($rows) + count($rows1) + count($rows2) + count($rows1sub);
- $this->assertEquals($countAllRows, $table->getRowsCountRecursive());
+ $this->assertEquals(3, $table->getRowsCount());
+ $this->assertEquals(18, $table->getRowsCountRecursive());
}
/**
* Simple test of the DataTable_Row
- *
- * @group Core
*/
public function testRow()
{
$columns = array('test_column' => 145,
- 092582495 => new Timer,
+ 92582495 => new Timer,
'super' => array('this column has an array value, amazing'));
$metadata = array('logo' => 'piwik.png',
'super' => array('this column has an array value, amazing'));
@@ -240,8 +231,6 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
/**
* Simple test of the DataTable_Row
- *
- * @group Core
*/
public function testSumRow()
{
@@ -258,14 +247,14 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
Row::COLUMNS => $columns,
Row::METADATA => $metadata,
'fake useless key' => 38959,
- 43905724897 => 'value');
+ '43905724897' => 'value');
$row1 = new Row($arrayRow);
$columns2 = array('test_int' => 5,
'test_float' => 4.5,
'test_float2' => 14.5,
'test_stringint' => "5",
- 0925824 => 'toto',
+ 925824 => 'toto',
'integerArrayToSum' => array(1 => 5, 2 => 5.5, 3 => array(2 => 4)),
);
$finalRow = new Row(array(Row::COLUMNS => $columns2));
@@ -277,7 +266,7 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
'test_stringint' => 150, //add also strings!!
'test' => 'string fake',
'integerArrayToSum' => array(1 => 6, 2 => 15.5, 3 => array(1 => 2, 2 => 7)),
- 0925824 => 'toto',
+ 925824 => 'toto',
);
// Also testing that metadata is copied over
@@ -285,8 +274,9 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
$this->assertTrue(Row::isEqual($rowWanted, $finalRow));
// testing that, 'sumRow' does not result in extra unwanted attributes being serialized
- $expectedRow = 'O:19:"Piwik\DataTable\Row":1:{s:1:"c";a:3:{i:0;a:8:{s:8:"test_int";i:150;s:10:"test_float";d:150;s:11:"test_float2";d:14.5;s:14:"test_stringint";i:150;i:0;s:4:"toto";s:17:"integerArrayToSum";a:3:{i:1;i:6;i:2;d:15.5;i:3;a:2:{i:2;i:7;i:1;i:2;}}s:11:"test_float3";d:1.5;s:4:"test";s:11:"string fake";}i:1;a:2:{s:4:"logo";s:9:"piwik.png";s:5:"super";a:1:{i:0;s:39:"this column has an array value, amazing";}}i:3;N;}}';
- $this->assertEquals($expectedRow, serialize($finalRow));
+
+ $expectedRow = 'a:3:{i:0;a:8:{s:8:"test_int";i:150;s:10:"test_float";d:150;s:11:"test_float2";d:14.5;s:14:"test_stringint";i:150;i:925824;s:4:"toto";s:17:"integerArrayToSum";a:3:{i:1;i:6;i:2;d:15.5;i:3;a:2:{i:2;i:7;i:1;i:2;}}s:11:"test_float3";d:1.5;s:4:"test";s:11:"string fake";}i:1;a:2:{s:4:"logo";s:9:"piwik.png";s:5:"super";a:1:{i:0;s:39:"this column has an array value, amazing";}}i:3;N;}';
+ $this->assertEquals($expectedRow, serialize($finalRow->export()));
// Testing sumRow with disabled metadata sum
$rowWanted = new Row(array(Row::COLUMNS => $columnsWanted)); // no metadata
@@ -296,26 +286,41 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
}
/**
- * @group Core
+ * @dataProvider unserializeTestsDataProvider
*/
- public function test_unserializeWorks_WhenDataTableFormatPriorPiwik2()
+ public function test_unserializeWorks_WithAllDataTableFormats($indexToRead, $isSummaryRow, $label, $column2, $subtable)
{
- $serializedDatatable = '';
- // Prior Piwik 2.0, we didn't use namespaces. Some
- require PIWIK_INCLUDE_PATH . "/tests/resources/pre-Piwik2-DataTable-archived.php";
+ $serializedDatatable = array();
+ // Prior Piwik 2.13, we serialized the actual Row or DataTableSummaryRow instances, afterwards only arrays
+ require PIWIK_INCLUDE_PATH . "/tests/resources/DataTables-archived-different-formats.php";
require_once PIWIK_INCLUDE_PATH . "/core/DataTable/Bridges.php";
- $this->assertTrue(strlen($serializedDatatable) > 1000);
+ $table = $serializedDatatable[$indexToRead];
+ $this->assertTrue(strlen($table) > 1000);
+
+ $table = DataTable::fromSerializedArray($table);
+ $row1 = $table->getFirstRow();
+ $this->assertTrue($row1 instanceof \Piwik\DataTable\Row);
+ $this->assertSame($isSummaryRow, $row1 instanceof \Piwik\DataTable\Row\DataTableSummaryRow); // we convert summary rows to Row instances
- $table = unserialize($serializedDatatable);
- $this->assertTrue($table[0] instanceof \Piwik\DataTable\Row);
+ $this->assertEquals($label, $row1->getColumn('label'));
+ $this->assertEquals($column2, $row1->getColumn(2));
+ $this->assertEquals($subtable, $row1->getIdSubDataTable());
+ }
+
+ public function unserializeTestsDataProvider()
+ {
+ return array(
+ array($index = 0, $isSummaryRow = false, $label = 'piwik.org', $column2 = 10509, $idSubtable = 1581), // pre Piwik 2.0 (without namespaces, Piwik_DataTable_Row)
+ array($index = 1, $isSummaryRow = true, $label = 'piwikactions.org', $column2 = 10508, $idSubtable = 1581), // pre Piwik 2.0 Actions (without namespaces, Piwik_DataTable_Row_DataTableSummary)
+ array($index = 2, $isSummaryRow = true, $label = 'start', $column2 = 89, $idSubtable = 2260), // >= Piwik 2.0 < Piwik 2.13 Actions (DataTableSummaryRow)
+ array($index = 3, $isSummaryRow = false, $label = 'Ask', $column2 = 11, $idSubtable = 3335), // >= Piwik 2.0 < Piwik 2.13 Referrers (Row)
+ array($index = 4, $isSummaryRow = false, $label = 'MyLabel Test', $column2 = 447, $idSubtable = 1), // >= Piwik 2.0 < Piwik 2.13 Referrers (Row)
+ );
}
/**
* Test that adding two string column values results in an exception.
- *
- * @group Core
- *
*/
public function testSumRow_stringException()
{
@@ -331,34 +336,28 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
$row2->sumRow($row1);
$this->assertTrue($noException = true);
-
}
/**
* Test serialize with an infinite recursion (a row linked to a table in the parent hierarchy)
* After 100 recursion must throw an exception
*
- * @group Core
- *
* @expectedException \Exception
*/
public function testSerializeWithInfiniteRecursion()
{
$table = new DataTable;
- $table->addRowFromArray(array(Row::COLUMNS => array('visits' => 245, 'visitors' => 245),
- Row::DATATABLE_ASSOCIATED => $table,));
+ $table->addRowFromArray(array(Row::COLUMNS => array('visits' => 245, 'visitors' => 245),
+ Row::DATATABLE_ASSOCIATED => $table));
$table->getSerialized();
}
/**
* Test queing filters
- *
- * @group Core
*/
public function testFilterQueueSortString()
{
-
$idcol = Row::COLUMNS;
$table = new DataTable;
@@ -410,8 +409,6 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
* We create some tables, add rows, some of the rows link to sub tables
*
* Then we serialize everything, and we check that the unserialize give the same object back
- *
- * @group Core
*/
public function testGeneral()
{
@@ -419,8 +416,7 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
* create some fake tables to make sure that the serialized array of the first TABLE
* does not take in consideration those tables
*/
- $useless1 = new DataTable;
- $useless1->addRowFromArray(array(Row::COLUMNS => array(13,),));
+ $useless1 = $this->createDataTable(array(array(13,)));
/*
* end fake tables
*/
@@ -431,7 +427,6 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
$table = new DataTable;
$subtable = new DataTable;
$idtable = $table->getId();
- $idsubtable = $subtable->getId();
/*
* create some fake tables to make sure that the serialized array of the first TABLE
@@ -439,8 +434,7 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
* -> we check that the DataTable_Manager is not impacting DataTable
*/
$useless1->addRowFromArray(array(Row::COLUMNS => array(8487,),));
- $useless3 = new DataTable;
- $useless3->addRowFromArray(array(Row::COLUMNS => array(8487,),));
+ $useless3 = $this->createDataTable(array(array(8487)));
/*
* end fake tables
*/
@@ -495,9 +489,9 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
$idsubsubtable = $subsubtable->getId();
- $serialized = ($table->getSerialized());
+ $serialized = $table->getSerialized();
- $this->assertEquals(array_keys($serialized), array($idsubsubtable, $idsubtable, 0));
+ $this->assertEquals(array_keys($serialized), array(2, 1, 0)); // subtableIds are now consecutive
// In the next test we compare an unserialized datatable with its original instance.
// The unserialized datatable rows will have positive DATATABLE_ASSOCIATED ids.
@@ -506,13 +500,13 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
// HOWEVER, because of datatable id conflicts happening in the datatable manager, it is not yet
// possible to know, after unserializing a datatable, if its sub-datatables are loaded in memory.
$expectedTableRows = array();
+ $i = 0;
foreach ($table->getRows() as $currentRow) {
$expectedTableRow = clone $currentRow;
- $currentRowAssociatedDatatableId = $currentRow->c[Row::DATATABLE_ASSOCIATED];
+ $currentRowAssociatedDatatableId = $currentRow->subtableId;
if ($currentRowAssociatedDatatableId != null) {
- // making DATATABLE_ASSOCIATED ids positive
- $expectedTableRow->c[Row::DATATABLE_ASSOCIATED] = -1 * $currentRowAssociatedDatatableId;
+ $expectedTableRow->setNonLoadedSubtableId(++$i); // subtableIds are consecutive
}
$expectedTableRows[] = $expectedTableRow;
@@ -524,15 +518,105 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($expectedTableRows, $tableAfter->getRows());
$subsubtableAfter = new DataTable;
- $subsubtableAfter->addRowsFromSerializedArray($serialized[$idsubsubtable]);
+ $subsubtableAfter->addRowsFromSerializedArray($serialized[$consecutiveSubtableId = 2]);
$this->assertEquals($subsubtable->getRows(), $subsubtableAfter->getRows());
- $this->assertEquals($subsubtable->getRows(), DataTable::fromSerializedArray($serialized[$idsubsubtable])->getRows());
+ $this->assertEquals($subsubtable->getRows(), DataTable::fromSerializedArray($serialized[$consecutiveSubtableId = 2])->getRows());
$this->assertTrue($subsubtable->getRowsCount() > 0);
$this->assertEquals($table, Manager::getInstance()->getTable($idtable));
$this->assertEquals($subsubtable, Manager::getInstance()->getTable($idsubsubtable));
}
+ public function test_getSerialized_shouldCreateConsecutiveSubtableIds()
+ {
+ $numRowsInRoot = 10;
+ $numRowsInSubtables = 5;
+
+ $rootTable = new DataTable();
+ $this->addManyRows($rootTable, 100);
+
+ foreach ($rootTable->getRows() as $row) {
+ $subtable = new DataTable();
+ $this->addManyRows($subtable, 100);
+ $row->setSubtable($subtable);
+
+ foreach ($subtable->getRows() as $subRow) {
+ $subRow->setSubtable(new DataTable());
+ }
+ }
+
+ // we want to make sure the tables have high ids but we will ignore them and just give them Ids starting from 0
+ $recentId = Manager::getInstance()->getMostRecentTableId();
+ $this->assertGreaterThanOrEqual(5000, $recentId);
+
+ $tables = $rootTable->getSerialized($numRowsInRoot, $numRowsInSubtables);
+
+ // make sure subtableIds are consecutive. Why "-1"? Because if we want 10 rows, there will be 9 subtables + 1 summary row which won't have a subtable
+ $sumSubTables = ($numRowsInRoot - 1) + (($numRowsInRoot - 1) * ($numRowsInSubtables - 1));
+ $subtableIds = array_keys($tables);
+ sort($subtableIds);
+ $this->assertEquals(range(0, $sumSubTables), $subtableIds);
+
+ // make sure the rows subtableId were updated as well.
+ foreach ($tables as $index => $serializedRows) {
+ $rows = unserialize($serializedRows);
+
+ if (0 === $index) {
+ // root table, make sure correct amount of rows are in subtables
+ $this->assertCount($numRowsInRoot, $rows);
+ }
+
+ foreach ($rows as $row) {
+ $subtableId = $row[Row::DATATABLE_ASSOCIATED];
+
+ if ($row[Row::COLUMNS]['label'] === DataTable::LABEL_SUMMARY_ROW) {
+ $this->assertNull($subtableId);
+ } else {
+
+ $this->assertLessThanOrEqual($sumSubTables, $subtableId); // make sure row was actually updated
+ $this->assertGreaterThanOrEqual(0, $subtableId);
+ $subrows = unserialize($tables[$subtableId]);
+
+ // this way we make sure the rows point to the correct subtable. only 2nd level rows have actually
+ // subtables. All 3rd level datatables do not have a row see table creation further above
+ if ($index === 0) {
+ $this->assertCount($numRowsInSubtables, $subrows);
+ } else {
+ $this->assertCount(0, $subrows);
+ }
+ }
+ }
+ }
+ }
+
+ public function test_getSerialized_shouldExportOnlyTheSerializedArrayOfAllTableRows()
+ {
+ $rootTable = new DataTable();
+ $this->addManyRows($rootTable, 2);
+
+ foreach ($rootTable->getRows() as $row) {
+ $subtable = new DataTable();
+ $this->addManyRows($subtable, 2);
+ $row->setSubtable($subtable);
+ }
+
+ $tables = $rootTable->getSerialized();
+
+ // we also make sure it actually handles the subtableIds correct etc
+ $this->assertEquals(array(
+ 0 => 'a:2:{i:0;a:3:{i:0;a:1:{s:5:"label";s:6:"label0";}i:1;a:0:{}i:3;i:1;}i:1;a:3:{i:0;a:1:{s:5:"label";s:6:"label1";}i:1;a:0:{}i:3;i:2;}}',
+ 1 => 'a:2:{i:0;a:3:{i:0;a:1:{s:5:"label";s:6:"label0";}i:1;a:0:{}i:3;N;}i:1;a:3:{i:0;a:1:{s:5:"label";s:6:"label1";}i:1;a:0:{}i:3;N;}}',
+ 2 => 'a:2:{i:0;a:3:{i:0;a:1:{s:5:"label";s:6:"label0";}i:1;a:0:{}i:3;N;}i:1;a:3:{i:0;a:1:{s:5:"label";s:6:"label1";}i:1;a:0:{}i:3;N;}}',
+ ), $tables);
+ }
+
+ private function addManyRows(DataTable $table, $numRows)
+ {
+ for ($i = 0; $i < $numRows; $i++) {
+ $table->addRowFromArray(array(Row::COLUMNS => array('label' => 'label' . $i)));
+ }
+ }
+
/**
* for all datatable->addDatatable tests we check that
* - row uniqueness is based on the label + presence of the SUBTABLE id
@@ -543,8 +627,6 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
/**
* add an empty datatable to a normal datatable
- *
- * @group Core
*/
public function testAddSimpleNoRowTable2()
{
@@ -557,8 +639,6 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
/**
* add a normal datatable to an empty datatable
- *
- * @group Core
*/
public function testAddSimpleNoRowTable1()
{
@@ -570,8 +650,6 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
/**
* add to the datatable another datatable// they don't have any row in common
- *
- * @group Core
*/
public function testAddSimpleNoCommonRow()
{
@@ -589,8 +667,6 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
/**
* add 2 datatable with some common rows
- *
- * @group Core
*/
public function testAddSimpleSomeCommonRow()
{
@@ -633,8 +709,6 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
/**
* add 2 datatable with only common rows
- *
- * @group Core
*/
public function testAddSimpleAllCommonRow()
{
@@ -674,12 +748,9 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
/**
* test add 2 different tables to the same table
- *
- * @group Core
*/
public function testAddDataTable2times()
{
-
$idcol = Row::COLUMNS;
$rows = array(
@@ -727,9 +798,6 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
$this->assertTrue(DataTable::isEqual($table, $tableExpected));
}
- /**
- * @group Core
- */
public function testUnrelatedDataTableNotDestructed()
{
$mockedDataTable = $this->getMock('\Piwik\DataTable', array('__destruct'));
@@ -741,28 +809,11 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
// from the database is in conflict with one of the Manager managed table identifiers.
// This is a rare but legitimate case as identifiers are not thoroughly synchronized
// when the expanded parameter is false.
- $rowBeingDestructed->c[Row::DATATABLE_ASSOCIATED] = $mockedDataTable->getId();
+ $rowBeingDestructed->subtableId = $mockedDataTable->getId();
Common::destroy($rowBeingDestructed);
}
- /**
- * @group Core
- */
- public function testGetSerializedCallsCleanPostSerialize()
- {
- $mockedDataTableRow = $this->getMock('\Piwik\DataTable\Row', array('cleanPostSerialize'));
- $mockedDataTableRow->expects($this->once())->method('cleanPostSerialize');
-
- $dataTableBeingSerialized = new DataTable();
- $dataTableBeingSerialized->addRow($mockedDataTableRow);
-
- $dataTableBeingSerialized->getSerialized();
- }
-
- /**
- * @group Core
- */
public function testSubDataTableIsDestructed()
{
$mockedDataTable = $this->getMock('\Piwik\DataTable', array('__destruct'));
@@ -774,16 +825,13 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
Common::destroy($rowBeingDestructed);
}
- /**
- * @group Core
- */
public function test_serializeFails_onSubTableNotFound()
{
// create a simple table with a subtable
$table1 = $this->_getDataTable1ForTest();
$table2 = $this->_getDataTable2ForTest();
$table2->getFirstRow()->setSubtable($table1);
- $idSubtable = $table1->getId();
+ $idSubtable = 1; // subtableIds are consecutive, we cannot use $table->getId()
/* Check it looks good:
$renderer = DataTable\Renderer::factory('xml');
@@ -798,13 +846,13 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
// both the main table and the sub table are serialized
$this->assertEquals(sizeof($serializedStrings), 2);
- $serialized = implode(",", $serializedStrings);
// the serialized string references the id subtable
- $this->assertTrue( false !== strpos($serialized, 'i:' . $idSubtable), "not found the id sub table in the serialized, not expected");
+ $unserialized = unserialize($serializedStrings[0]);
+ $this->assertSame($idSubtable, $unserialized[0][3], "not found the id sub table in the serialized, not expected");
// KABOOM, we delete the subtable, reproducing a "random data issue"
- Manager::getInstance()->deleteTable($idSubtable);
+ Manager::getInstance()->deleteTable($table1->getId());
// Now we will serialize this "broken datatable" and check it works.
@@ -813,11 +861,20 @@ class DataTableTest extends \PHPUnit_Framework_TestCase
// - the serialized table does NOT contain the sub table
$this->assertEquals(sizeof($serializedStrings), 1); // main table only is serialized
- $serialized = implode(",", $serializedStrings);
+ $unserialized = unserialize($serializedStrings[0]);
// - the serialized string does NOT contain the id subtable (the row was cleaned up as expected)
- $this->assertTrue( false === strpos($serialized, 'i:' . $idSubtable), "found the id sub table in the serialized, not expected");
+ $this->assertNull($unserialized[0][3], "found the id sub table in the serialized, not expected");
+ }
+
+ private function createDataTable($rows)
+ {
+ $useless1 = new DataTable;
+ foreach ($rows as $row) {
+ $useless1->addRowFromArray(array(Row::COLUMNS => $row));
+ }
+ return $useless1;
}
protected function _getDataTable1ForTest()
diff --git a/tests/PHPUnit/Unit/DbTest.php b/tests/PHPUnit/Unit/DbTest.php
deleted file mode 100644
index 70b4338866..0000000000
--- a/tests/PHPUnit/Unit/DbTest.php
+++ /dev/null
@@ -1,39 +0,0 @@
-<?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\Unit\Db;
-
-use Piwik\Db;
-
-class DbTest extends \PHPUnit_Framework_TestCase
-{
- /**
- * @dataProvider getIsOptimizeInnoDBTestData
- */
- public function test_isOptimizeInnoDBSupported_ReturnsCorrectResult($version, $expectedResult)
- {
- $result = Db::isOptimizeInnoDBSupported($version);
- $this->assertEquals($expectedResult, $result);
- }
-
- public function getIsOptimizeInnoDBTestData()
- {
- return array(
- array("10.0.17-MariaDB-1~trusty", false),
- array("10.1.1-MariaDB-1~trusty", true),
- array("10.2.0-MariaDB-1~trusty", true),
- array("10.6.19-0ubuntu0.14.04.1", false),
-
- // for sanity. maybe not ours.
- array("", false),
- array(0, false),
- array(false, false),
- array("slkdf(@*#lkesjfMariaDB", false),
- array("slkdfjq3rujlkv", false),
- );
- }
-} \ No newline at end of file
diff --git a/tests/PHPUnit/Unit/DeprecatedMethodsTest.php b/tests/PHPUnit/Unit/DeprecatedMethodsTest.php
index f1a03890c8..838b4e9479 100644
--- a/tests/PHPUnit/Unit/DeprecatedMethodsTest.php
+++ b/tests/PHPUnit/Unit/DeprecatedMethodsTest.php
@@ -60,6 +60,9 @@ class DeprecatedMethodsTest extends \PHPUnit_Framework_TestCase
$this->assertDeprecatedMethodIsRemoved('Piwik\Plugins\UserSettings\API', 'getLanguageCode', $validTill);
$this->assertDeprecatedMethodIsRemoved('Piwik\Plugins\UserSettings\UserSettings', 'renameDeprecatedModuleAndAction', $validTill);
+ $validTill = '2015-06-01';
+ $this->assertDeprecatedMethodIsRemoved('Piwik\Archive', 'getBlob', $validTill);
+
$this->assertDeprecatedMethodIsRemovedInPiwik3('\Piwik\Menu\MenuAbstract', 'add');
$this->assertDeprecatedMethodIsRemovedInPiwik3('\Piwik\Archive', 'getDataTableFromArchive');
}