12bde879aSAndreas Gohr<?php 22bde879aSAndreas Gohr 32bde879aSAndreas Gohrnamespace dokuwiki\test\ChangeLog; 42bde879aSAndreas Gohr 52bde879aSAndreas Gohruse dokuwiki\ChangeLog\PageChangeLog; 62bde879aSAndreas Gohr 72bde879aSAndreas Gohr/** 82bde879aSAndreas Gohr * Tests for dokuwiki\ChangeLog\PageChangeLog. 92bde879aSAndreas Gohr */ 102bde879aSAndreas Gohrclass PageChangeLogTest extends \DokuWikiTest 112bde879aSAndreas Gohr{ 122bde879aSAndreas Gohr /** 132bde879aSAndreas Gohr * A page deleted through DokuWiki is recorded as its own revision, newer than the 142bde879aSAndreas Gohr * last revision that still had content. getRelativeRevision() must walk back from 152bde879aSAndreas Gohr * that deletion entry to the last content revision (issue #4635). 162bde879aSAndreas Gohr */ 172bde879aSAndreas Gohr public function testRevisionBeforeNormalDeletion() 182bde879aSAndreas Gohr { 192bde879aSAndreas Gohr $page = 'changelog_deleted'; 202bde879aSAndreas Gohr saveWikiText($page, 'first content', 'create', false); 212bde879aSAndreas Gohr $this->waitForTick(true); 222bde879aSAndreas Gohr saveWikiText($page, 'second content longer', 'edit', false); 232bde879aSAndreas Gohr $this->waitForTick(true); 242bde879aSAndreas Gohr 252bde879aSAndreas Gohr $editRev = (new PageChangeLog($page))->currentRevision(); 262bde879aSAndreas Gohr 272bde879aSAndreas Gohr saveWikiText($page, '', 'delete', false); 282bde879aSAndreas Gohr clearstatcache(); 292bde879aSAndreas Gohr 302bde879aSAndreas Gohr $changelog = new PageChangeLog($page); 312bde879aSAndreas Gohr $delRev = $changelog->currentRevision(); 322bde879aSAndreas Gohr 332bde879aSAndreas Gohr $this->assertNotEquals($editRev, $delRev, 'deletion should get its own revision'); 342bde879aSAndreas Gohr $this->assertEquals( 352bde879aSAndreas Gohr DOKU_CHANGE_TYPE_DELETE, 362bde879aSAndreas Gohr $changelog->getRevisionInfo($delRev)['type'], 372bde879aSAndreas Gohr 'current revision should be the deletion' 382bde879aSAndreas Gohr ); 392bde879aSAndreas Gohr $this->assertEquals( 402bde879aSAndreas Gohr $editRev, 412bde879aSAndreas Gohr $changelog->getRelativeRevision($delRev, -1), 422bde879aSAndreas Gohr 'the revision before the deletion should be the last edit' 432bde879aSAndreas Gohr ); 442bde879aSAndreas Gohr } 452bde879aSAndreas Gohr 462bde879aSAndreas Gohr /** 472bde879aSAndreas Gohr * An external deletion is detected and persisted on first read as its own revision 482bde879aSAndreas Gohr * with an unknown exact date, newer than the last content revision. 492bde879aSAndreas Gohr * getRelativeRevision() must walk back from it to that last content revision 502bde879aSAndreas Gohr * (issue #4635). 512bde879aSAndreas Gohr */ 522bde879aSAndreas Gohr public function testRevisionBeforeExternalDeletion() 532bde879aSAndreas Gohr { 542bde879aSAndreas Gohr $page = 'changelog_extdeleted'; 552bde879aSAndreas Gohr saveWikiText($page, 'first content', 'create', false); 562bde879aSAndreas Gohr $this->waitForTick(true); 572bde879aSAndreas Gohr saveWikiText($page, 'second content longer', 'edit', false); 582bde879aSAndreas Gohr $this->waitForTick(true); 592bde879aSAndreas Gohr 602bde879aSAndreas Gohr $editRev = (new PageChangeLog($page))->currentRevision(); 612bde879aSAndreas Gohr 622bde879aSAndreas Gohr // delete the page file externally, bypassing DokuWiki 632bde879aSAndreas Gohr unlink(wikiFN($page)); 642bde879aSAndreas Gohr clearstatcache(); 652bde879aSAndreas Gohr 662bde879aSAndreas Gohr // first read detects and persists the external deletion 672bde879aSAndreas Gohr $changelog = new PageChangeLog($page); 682bde879aSAndreas Gohr $delRev = $changelog->currentRevision(); 692bde879aSAndreas Gohr $delInfo = $changelog->getRevisionInfo($delRev); 702bde879aSAndreas Gohr 712bde879aSAndreas Gohr $this->assertNotEquals($editRev, $delRev, 'external deletion should get its own revision'); 722bde879aSAndreas Gohr $this->assertEquals(DOKU_CHANGE_TYPE_DELETE, $delInfo['type'], 'current revision should be the deletion'); 732bde879aSAndreas Gohr $this->assertFalse($delInfo['timestamp'], 'external deletion has an unknown exact date'); 742bde879aSAndreas Gohr $this->assertEquals( 752bde879aSAndreas Gohr $editRev, 762bde879aSAndreas Gohr $changelog->getRelativeRevision($delRev, -1), 772bde879aSAndreas Gohr 'the revision before the external deletion should be the last edit' 782bde879aSAndreas Gohr ); 792bde879aSAndreas Gohr } 800a245329SAndreas Gohr 810a245329SAndreas Gohr /** 820a245329SAndreas Gohr * A current revision's file can have its modification time bumped without any content 830a245329SAndreas Gohr * change (a backup restore, a git checkout, ...). That must not be recorded as an 840a245329SAndreas Gohr * external edit: the content is compared against the last revision and, when identical, 850a245329SAndreas Gohr * the file mtime is reset to the recorded revision date instead (issue #4634). 860a245329SAndreas Gohr */ 870a245329SAndreas Gohr public function testTouchedFileWithUnchangedContentIsNotExternalEdit() 880a245329SAndreas Gohr { 890a245329SAndreas Gohr $page = 'changelog_touched'; 900a245329SAndreas Gohr saveWikiText($page, 'first content', 'create', false); 910a245329SAndreas Gohr 920a245329SAndreas Gohr $changelog = new PageChangeLog($page); 930a245329SAndreas Gohr $lastRev = $changelog->currentRevision(); 940a245329SAndreas Gohr 950a245329SAndreas Gohr // bump the file mtime forward without changing the content 960a245329SAndreas Gohr touch(wikiFN($page), $lastRev + 1000); 970a245329SAndreas Gohr clearstatcache(); 980a245329SAndreas Gohr 990a245329SAndreas Gohr $changelog = new PageChangeLog($page); 1000a245329SAndreas Gohr $currentRev = $changelog->currentRevision(); 1010a245329SAndreas Gohr $currentInfo = $changelog->getRevisionInfo($currentRev); 1020a245329SAndreas Gohr 1030a245329SAndreas Gohr $this->assertEquals($lastRev, $currentRev, 'unchanged content must not create an external revision'); 1040a245329SAndreas Gohr $this->assertArrayNotHasKey('timestamp', $currentInfo, 'should not be a synthesized external edit'); 1050a245329SAndreas Gohr $this->assertCount(1, $changelog->getRevisions(-1, 200), 'no external edit entry should be added'); 1060a245329SAndreas Gohr 1070a245329SAndreas Gohr clearstatcache(); 1080a245329SAndreas Gohr $this->assertEquals($lastRev, filemtime(wikiFN($page)), 'file mtime should be reset to the changelog date'); 1090a245329SAndreas Gohr } 110*eab6268cSAndreas Gohr 111*eab6268cSAndreas Gohr /** 112*eab6268cSAndreas Gohr * A detected external edit whose date predates the most recent change already recorded 113*eab6268cSAndreas Gohr * in the global changelog must stay out of the recent-changes feed (or it would appear 114*eab6268cSAndreas Gohr * above newer entries with an old date), but is still recorded in the page's own 115*eab6268cSAndreas Gohr * changelog (issue #4634). 116*eab6268cSAndreas Gohr */ 117*eab6268cSAndreas Gohr public function testOutOfOrderExternalEditKeptOutOfGlobalChangelog() 118*eab6268cSAndreas Gohr { 119*eab6268cSAndreas Gohr global $conf; 120*eab6268cSAndreas Gohr $page = 'changelog_outoforder'; 121*eab6268cSAndreas Gohr saveWikiText($page, 'first content', 'create', false); 122*eab6268cSAndreas Gohr 123*eab6268cSAndreas Gohr $changelog = new PageChangeLog($page); 124*eab6268cSAndreas Gohr $createRev = $changelog->currentRevision(); 125*eab6268cSAndreas Gohr 126*eab6268cSAndreas Gohr // external edit with different content, dated after the create but well before the 127*eab6268cSAndreas Gohr // global changelog's last-modified time 128*eab6268cSAndreas Gohr $globalFile = $conf['changelog']; 129*eab6268cSAndreas Gohr $extRev = $createRev + 10; 130*eab6268cSAndreas Gohr file_put_contents(wikiFN($page), 'externally edited content'); 131*eab6268cSAndreas Gohr touch(wikiFN($page), $extRev); 132*eab6268cSAndreas Gohr touch($globalFile, $createRev + 100000); 133*eab6268cSAndreas Gohr clearstatcache(); 134*eab6268cSAndreas Gohr 135*eab6268cSAndreas Gohr $changelog = new PageChangeLog($page); 136*eab6268cSAndreas Gohr $detectedRev = $changelog->currentRevision(); 137*eab6268cSAndreas Gohr $detectedInfo = $changelog->getRevisionInfo($detectedRev); 138*eab6268cSAndreas Gohr 139*eab6268cSAndreas Gohr // detected and recorded in the page's own changelog: the create plus the external edit 140*eab6268cSAndreas Gohr $this->assertEquals($extRev, $detectedRev, 'external edit should be detected at the file mtime'); 141*eab6268cSAndreas Gohr $this->assertEquals(DOKU_CHANGE_TYPE_EDIT, $detectedInfo['type'], 'should be an external edit'); 142*eab6268cSAndreas Gohr $this->assertEquals( 143*eab6268cSAndreas Gohr [$extRev, $createRev], 144*eab6268cSAndreas Gohr $changelog->getRevisions(-1, 200), 145*eab6268cSAndreas Gohr 'page changelog should hold the create and the external edit' 146*eab6268cSAndreas Gohr ); 147*eab6268cSAndreas Gohr 148*eab6268cSAndreas Gohr // ...but kept out of the global recent-changes feed 149*eab6268cSAndreas Gohr $this->assertStringNotContainsString( 150*eab6268cSAndreas Gohr "$extRev\t", 151*eab6268cSAndreas Gohr file_get_contents($globalFile), 152*eab6268cSAndreas Gohr 'out-of-order external edit must not be appended to the global changelog' 153*eab6268cSAndreas Gohr ); 154*eab6268cSAndreas Gohr } 155*eab6268cSAndreas Gohr 156*eab6268cSAndreas Gohr /** 157*eab6268cSAndreas Gohr * A genuinely current external edit (dated at or after the global changelog's last 158*eab6268cSAndreas Gohr * recorded change) must still reach the recent-changes feed (issue #4634). 159*eab6268cSAndreas Gohr */ 160*eab6268cSAndreas Gohr public function testCurrentExternalEditReachesGlobalChangelog() 161*eab6268cSAndreas Gohr { 162*eab6268cSAndreas Gohr global $conf; 163*eab6268cSAndreas Gohr $page = 'changelog_freshext'; 164*eab6268cSAndreas Gohr saveWikiText($page, 'first content', 'create', false); 165*eab6268cSAndreas Gohr 166*eab6268cSAndreas Gohr $changelog = new PageChangeLog($page); 167*eab6268cSAndreas Gohr $createRev = $changelog->currentRevision(); 168*eab6268cSAndreas Gohr 169*eab6268cSAndreas Gohr // external edit dated after the create, so it is newer than the feed's most recent 170*eab6268cSAndreas Gohr // change (the create just written there) and should be appended 171*eab6268cSAndreas Gohr $extRev = $createRev + 100; 172*eab6268cSAndreas Gohr file_put_contents(wikiFN($page), 'externally edited content'); 173*eab6268cSAndreas Gohr touch(wikiFN($page), $extRev); 174*eab6268cSAndreas Gohr clearstatcache(); 175*eab6268cSAndreas Gohr 176*eab6268cSAndreas Gohr $changelog = new PageChangeLog($page); 177*eab6268cSAndreas Gohr $changelog->currentRevision(); 178*eab6268cSAndreas Gohr 179*eab6268cSAndreas Gohr $this->assertStringContainsString( 180*eab6268cSAndreas Gohr "$extRev\t", 181*eab6268cSAndreas Gohr file_get_contents($conf['changelog']), 182*eab6268cSAndreas Gohr 'a current external edit should be appended to the global changelog' 183*eab6268cSAndreas Gohr ); 184*eab6268cSAndreas Gohr } 1852bde879aSAndreas Gohr} 186