1<?php 2 3use dokuwiki\ChangeLog\PageChangeLog; 4 5/** 6 * saveWikiText() stores files in pages/, attic/ and adds entries to changelog 7 */ 8class common_saveWikiText_test extends DokuWikiTest { 9 10 /** Delay writes of old revisions by a second. */ 11 public function handle_write(Doku_Event $event, $param) { 12 if ($event->data[3] !== false) { 13 $this->waitForTick(); 14 } 15 } 16 17 /** 18 * assertions against changelog entries and attic after saveWikiText() 19 */ 20 private function checkChangeLogAfterNormalSave( 21 PageChangeLog $pagelog, 22 $expectedRevs, 23 &$expectedLastEntry 24 ) { 25 $revisions = $pagelog->getRevisions(-1, 200); 26 $this->assertCount($expectedRevs, $revisions); 27 $this->assertCount($expectedRevs, array_unique($revisions), 'date duplicated in changelog'); 28 // last revision 29 $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]); 30 $expectedLastEntry += $lastRevInfo; 31 $this->assertEquals($expectedLastEntry, $lastRevInfo); 32 // current revision 33 $currentRevInfo = $pagelog->getCurrentRevisionInfo(); 34 $this->assertEquals($currentRevInfo, $lastRevInfo, 'current & last revs should be identical'); 35 // attic 36 $attic = wikiFN($lastRevInfo['id'], $lastRevInfo['date']); 37 $this->assertFileExists($attic, 'file missing in attic'); 38 $files = count(glob(dirname($attic).'/'.noNS($lastRevInfo['id']).'.*')); 39 $this->assertLessThanOrEqual($expectedRevs, $files, 'detectExternalEdit() should not add too often old revs'); 40 } 41 42 /** 43 * assertions against changelog entries and attic after external edit, create or deletion of page 44 */ 45 private function checkChangeLogAfterExternalEdit( 46 PageChangeLog $pagelog, 47 $expectedRevs, 48 $expectedLastEntry, 49 $expectedCurrentEntry 50 ) { 51 $revisions = $pagelog->getRevisions(-1, 200); 52 $this->assertCount($expectedRevs, $revisions); 53 $this->assertCount($expectedRevs, array_unique($revisions), 'date duplicated in changelog'); 54 // last revision 55 if ($expectedRevs > 0) { 56 $lastRevInfo = $pagelog->getRevisionInfo($revisions[0]); 57 $expectedLastEntry += $lastRevInfo; 58 $this->assertEquals($expectedLastEntry, $lastRevInfo); 59 } else { 60 $this->assertFalse($pagelog->lastRevision(), 'changelog file does not yet exist'); 61 } 62 // current revision 63 $currentRevInfo = $pagelog->getCurrentRevisionInfo(); 64 $this->assertArrayHasKey('timestamp', $currentRevInfo, 'should be external revision'); 65 $expectedCurrentEntry += $currentRevInfo; 66 if ($expectedRevs > 0) { 67 $this->assertEquals($expectedCurrentEntry, $currentRevInfo); 68 69 } 70 // attic 71 $attic = wikiFN($currentRevInfo['id'], $currentRevInfo['date']); 72 $this->assertFileNotExists($attic, 'page does not yet exist in attic'); 73 } 74 75 76 /** 77 * Execute a whole bunch of saves on the same page and check the results 78 * TEST 1 79 * 1.1 create a page 80 * 1.2 save with same content should be ignored 81 * 1.3 update the page with new text 82 * 1.4 add a minor edit (unauthenticated, minor not allowable) 83 * 1.5 add a minor edit (authenticated) 84 * 1.6 delete 85 * 1.7 restore 86 * 1.8 external edit 87 * 1.9 edit and save on top of external edit 88 */ 89 function test_savesequence1() { 90 global $REV; 91 92 $page = 'page'; 93 $file = wikiFN($page); 94 $this->assertFileNotExists($file); 95 96 // 1.1 create a page 97 saveWikiText($page, 'teststring', '1st save', false); 98 $this->assertFileExists($file); 99 $lastmod = filemtime($file); 100 $expectedRevs = 1; 101 $expect = array( 102 'date' => $lastmod, 103 'type' => DOKU_CHANGE_TYPE_CREATE, 104 'sum' => '1st save', 105 'sizechange' => 10, // = strlen('teststring') 106 ); 107 108 $pagelog = new PageChangeLog($page); 109 $this->checkChangeLogAfterNormalSave($pagelog, $expectedRevs, $expect); 110 111 $this->waitForTick(true); // wait for new revision ID 112 113 // 1.2 save with same content should be ignored 114 saveWikiText($page, 'teststring', '2nd save', false); 115 clearstatcache(false, $file); 116 $this->assertEquals($lastmod, filemtime($file)); 117 118 $pagelog = new PageChangeLog($page); 119 $revisions = $pagelog->getRevisions(-1, 200); 120 $this->assertCount(1, $revisions); 121 122 // 1.3 update the page with new text 123 saveWikiText($page, 'teststring2long', '3rd save', false); 124 clearstatcache(false, $file); 125 $newmod = filemtime($file); 126 $this->assertNotEquals($lastmod, $newmod); 127 $lastmod = $newmod; 128 $expectedRevs = 2; 129 $expect = array( 130 'date' => $lastmod, 131 'type' => DOKU_CHANGE_TYPE_EDIT, 132 'sum' => '3rd save', 133 'sizechange' => 5, 134 ); 135 136 $pagelog = new PageChangeLog($page); 137 $this->checkChangeLogAfterNormalSave($pagelog, $expectedRevs, $expect); 138 139 $this->waitForTick(); // wait for new revision ID 140 141 // 1.4 add a minor edit (unauthenticated, minor not allowable) 142 saveWikiText($page, 'teststring3long', '4th save', true); 143 clearstatcache(false, $file); 144 $newmod = filemtime($file); 145 $this->assertNotEquals($lastmod, $newmod); 146 $lastmod = $newmod; 147 $expectedRevs = 3; 148 $expect = array( 149 'date' => $lastmod, 150 'type' => DOKU_CHANGE_TYPE_EDIT, 151 'sum' => '4th save', 152 'sizechange' => 0, 153 ); 154 155 $pagelog = new PageChangeLog($page); 156 $this->checkChangeLogAfterNormalSave($pagelog, $expectedRevs, $expect); 157 158 $this->waitForTick(); // wait for new revision ID 159 160 // 1.5 add a minor edit (authenticated) 161 $_SERVER['REMOTE_USER'] = 'user'; 162 saveWikiText($page, 'teststring4', '5th save', true); 163 clearstatcache(false, $file); 164 $newmod = filemtime($file); 165 $this->assertNotEquals($lastmod, $newmod); 166 $lastmod = $newmod; 167 $expectedRevs = 4; 168 $expect = array( 169 'date' => $lastmod, 170 'type' => DOKU_CHANGE_TYPE_MINOR_EDIT, 171 'sum' => '5th save', 172 'sizechange' => -4, 173 ); 174 175 $pagelog = new PageChangeLog($page); 176 $this->checkChangeLogAfterNormalSave($pagelog, $expectedRevs, $expect); 177 178 $this->waitForTick(); // wait for new revision ID 179 180 // 1.6 delete 181 saveWikiText($page, '', '6th save', false); 182 clearstatcache(false, $file); 183 $this->assertFileNotExists($file); 184 $expectedRevs = 5; 185 $expect = array( 186 //'date' => $lastmod, // ignore from lastRev assertion, but confirm attic file existence 187 'type' => DOKU_CHANGE_TYPE_DELETE, 188 'sum' => '6th save', 189 'sizechange' => -11, 190 ); 191 192 $pagelog = new PageChangeLog($page); 193 $this->checkChangeLogAfterNormalSave($pagelog, $expectedRevs, $expect); 194 195 $this->waitForTick(); // wait for new revision ID 196 197 // 1.7 restore 198 $REV = $lastmod; 199 saveWikiText($page, 'teststring4', '7th save', true); 200 clearstatcache(false, $file); 201 $this->assertFileExists($file); 202 $newmod = filemtime($file); 203 $this->assertNotEquals($lastmod, $newmod); 204 $lastmod = $newmod; 205 $expectedRevs = 6; 206 $expect = array( 207 'date' => $lastmod, 208 'type' => DOKU_CHANGE_TYPE_REVERT, 209 'sum' => '7th save', 210 'sizechange' => 11, 211 ); 212 213 $pagelog = new PageChangeLog($page); 214 $this->checkChangeLogAfterNormalSave($pagelog, $expectedRevs, $expect); 215 $REV = ''; 216 217 $this->waitForTick(); // wait for new revision ID 218 219 // 1.8 external edit 220 file_put_contents($file, 'teststring5 external edit'); 221 clearstatcache(false, $file); 222 $newmod = filemtime($file); 223 $this->assertNotEquals($lastmod, $newmod); 224 $lastmod = $newmod; 225 $expectedRevs = 6; // external edit is not yet in changelog 226 $expectExternal = array( 227 'date' => $lastmod, 228 'type' => DOKU_CHANGE_TYPE_EDIT, 229 'sum' => 'external edit', 230 'sizechange' => 14, 231 ); 232 233 $pagelog = new PageChangeLog($page); 234 $this->checkChangeLogAfterExternalEdit($pagelog, $expectedRevs, $expect, $expectExternal); 235 236 $this->waitForTick(); // wait for new revision ID 237 238 // 1.9 save on top of external edit 239 saveWikiText($page, 'teststring6', '8th save', false); 240 clearstatcache(false, $file); 241 $newmod = filemtime($file); 242 $this->assertNotEquals($lastmod, $newmod); 243 $lastmod = $newmod; 244 $expectedRevs = 8; 245 $expect = array( 246 'date' => $lastmod, 247 'type' => DOKU_CHANGE_TYPE_EDIT, 248 'sum' => '8th save', 249 'sizechange' => -14, 250 ); 251 252 $pagelog = new PageChangeLog($page); 253 $this->checkChangeLogAfterNormalSave($pagelog, $expectedRevs, $expect); 254 } 255 256 /** 257 * Execute a whole bunch of saves on the same page and check the results 258 * using $this->handle_write() in event IO_WIKIPAGE_WRITE 259 * TEST 2 - create a page externally in 2.3, while external edit in Test 1.8 260 * 2.1 create a page 261 * 2.2 delete 262 * 2.3 externally create the page 263 * 2.4 edit and save on top of external edit 264 * 2.5 external edit 265 * 2.6 edit and save on top of external edit, again 266 */ 267 function test_savesequence2() { 268 // add an additional delay when saving files to make sure 269 // nobody relies on the saving happening in the same second 270 /** @var $EVENT_HANDLER \dokuwiki\Extension\EventHandler */ 271 global $EVENT_HANDLER; 272 $EVENT_HANDLER->register_hook('IO_WIKIPAGE_WRITE', 'BEFORE', $this, 'handle_write'); 273 274 $page = 'page2'; 275 $file = wikiFN($page); 276 $this->assertFileNotExists($file); 277 278 // 2.1 create a page 279 saveWikiText($page, 'teststring', 'Test 2, 1st save', false); 280 $this->assertFileExists($file); 281 $lastmod = filemtime($file); 282 $expectedRevs = 1; 283 $expect = array( 284 'date' => $lastmod, 285 'type' => DOKU_CHANGE_TYPE_CREATE, 286 'sum' => 'Test 2, 1st save', 287 'sizechange' => 10, // = strlen('teststring') 288 ); 289 290 $pagelog = new PageChangeLog($page); 291 $this->checkChangeLogAfterNormalSave($pagelog, $expectedRevs, $expect); 292 293 $this->waitForTick(true); // wait for new revision ID 294 295 // 2.2 delete 296 saveWikiText($page, '', 'Test 2, 2nd save', false); 297 clearstatcache(false, $file); 298 $this->assertFileNotExists($file); 299 $expectedRevs = 2; 300 $expect = array( 301 //'date' => $lastmod, // ignore from lastRev assertion, but confirm attic file existence 302 'type' => DOKU_CHANGE_TYPE_DELETE, 303 'sum' => 'Test 2, 2nd save', 304 'sizechange' => -10, 305 ); 306 307 $pagelog = new PageChangeLog($page); 308 $this->checkChangeLogAfterNormalSave($pagelog, $expectedRevs, $expect); 309 310 $this->waitForTick(); // wait for new revision ID 311 312 // 2.3 externally create the page 313 file_put_contents($file, 'teststring5'); 314 clearstatcache(false, $file); 315 $lastmod = filemtime($file); 316 $expectedRevs = 2; // external edit is not yet in changelog 317 $expectExternal = array( 318 'date' => $lastmod, 319 'type' => DOKU_CHANGE_TYPE_CREATE, 320 'sum' => 'created - external edit', 321 'sizechange' => 11, 322 ); 323 324 $pagelog = new PageChangeLog($page); 325 $this->checkChangeLogAfterExternalEdit($pagelog, $expectedRevs, $expect, $expectExternal); 326 327 $this->waitForTick(); // wait for new revision ID 328 329 // 2.4 save on top of external edit 330 saveWikiText($page, 'teststring6', 'Test 2, 3rd save', false); 331 clearstatcache(false, $file); 332 $newmod = filemtime($file); 333 $this->assertNotEquals($lastmod, $newmod); 334 $lastmod = $newmod; 335 $expectedRevs = 4; // two more revisions now! 336 $expect = array( 337 'date' => $lastmod, 338 'type' => DOKU_CHANGE_TYPE_EDIT, 339 'sum' => 'Test 2, 3rd save', 340 'sizechange' => 0, 341 ); 342 343 $pagelog = new PageChangeLog($page); 344 $this->checkChangeLogAfterNormalSave($pagelog, $expectedRevs, $expect); 345 346 $this->waitForTick(); // wait for new revision ID 347 348 // 2.5 external edit 349 file_put_contents($file, 'teststring7 external edit2'); 350 clearstatcache(false, $file); 351 $newmod = filemtime($file); 352 $this->assertNotEquals($lastmod, $newmod); 353 $lastmod = $newmod; 354 $expectedRevs = 4; // external edit is not yet in changelog 355 $expectExternal = array( 356 'date' => $lastmod, 357 'type' => DOKU_CHANGE_TYPE_EDIT, 358 'sum' => 'external edit', 359 'sizechange' => 15, 360 ); 361 362 $pagelog = new PageChangeLog($page); 363 $this->checkChangeLogAfterExternalEdit($pagelog, $expectedRevs, $expect, $expectExternal); 364 365 $this->waitForTick(); // wait for new revision ID 366 367 // 2.6 save on top of external edit, again 368 saveWikiText($page, 'teststring8', 'Test 2, 4th save', false); 369 clearstatcache(false, $file); 370 $newmod = filemtime($file); 371 $this->assertNotEquals($lastmod, $newmod); 372 $lastmod = $newmod; 373 $expectedRevs = 6; // two more revisions now! 374 $expect = array( 375 'date' => $lastmod, 376 'type' => DOKU_CHANGE_TYPE_EDIT, 377 'sum' => 'Test 2, 4th save', 378 'sizechange' => -15, 379 ); 380 381 $pagelog = new PageChangeLog($page); 382 $this->checkChangeLogAfterNormalSave($pagelog, $expectedRevs, $expect); 383 } 384 385 /** 386 * Execute a whole bunch of saves on the same page and check the results 387 * TEST 3 - typical page life of bundled page such as wiki:syntax 388 * 3.1 externally create a page 389 * 3.2 external edit 390 * 3.3 edit and save on top of external edit 391 * 3.4 externally delete the page 392 */ 393 function test_savesequence3() { 394 $page = 'page3'; 395 $file = wikiFN($page); 396 397 // 3.1 externally create a page 398 $this->assertFileNotExists($file); 399 file_put_contents($file, 'teststring'); 400 clearstatcache(false, $file); 401 $lastmod = filemtime($file); 402 $expectedRevs = 0; // external edit is not yet in changelog 403 $expect = false; 404 $expectExternal = array( 405 'date' => $lastmod, 406 'type' => DOKU_CHANGE_TYPE_CREATE, 407 'sum' => 'created - external edit', 408 'sizechange' => 10, 409 ); 410 411 $pagelog = new PageChangeLog($page); 412 $this->checkChangeLogAfterExternalEdit($pagelog, $expectedRevs, $expect, $expectExternal); 413 414 $this->waitForTick(true); // wait for new revision ID 415 416 // 3.2 external edit (repeated, still no changelog exists) 417 file_put_contents($file, 'teststring external edit'); 418 clearstatcache(false, $file); 419 $newmod = filemtime($file); 420 $this->assertNotEquals($lastmod, $newmod); 421 $lastmod = $newmod; 422 $expectedRevs = 0; // external edit is not yet in changelog 423 $expectExternal = array( 424 'date' => $lastmod, 425 'type' => DOKU_CHANGE_TYPE_CREATE, // not DOKU_CHANGE_TYPE_EDIT 426 'sum' => 'created - external edit', 427 'sizechange' => 24, 428 ); 429 430 $pagelog = new PageChangeLog($page); 431 $this->checkChangeLogAfterExternalEdit($pagelog, $expectedRevs, $expect, $expectExternal); 432 433 $this->waitForTick(true); // wait for new revision ID 434 435 // 3.3 save on top of external edit 436 saveWikiText($page, 'teststring1', 'Test 3, first save', false); 437 clearstatcache(false, $file); 438 $newmod = filemtime($file); 439 $this->assertNotEquals($lastmod, $newmod); 440 $lastmod = $newmod; 441 $expectedRevs = 2; // two more revisions now! 442 $expect = array( 443 'date' => $lastmod, 444 'type' => DOKU_CHANGE_TYPE_EDIT, 445 'sum' => 'Test 3, first save', 446 'sizechange' => -13, 447 ); 448 449 $pagelog = new PageChangeLog($page); 450 $this->checkChangeLogAfterNormalSave($pagelog, $expectedRevs, $expect); 451 452 453 $this->waitForTick(true); // wait for new revision ID 454 455 // 3.4 externally delete the page 456 unlink($file); 457 clearstatcache(false, $file); 458 $expectedRevs = 2; 459 $expectExternal = array( 460 //'date' => $lastmod, 461 'type' => DOKU_CHANGE_TYPE_DELETE, 462 'sum' => 'removed - external edit (Unknown date)', 463 'sizechange' => -11, 464 ); 465 466 $pagelog = new PageChangeLog($page); 467 $this->checkChangeLogAfterExternalEdit($pagelog, $expectedRevs, $expect, $expectExternal); 468 } 469 470} 471