1<?php 2 3/** 4 * webtrees: online genealogy 5 * Copyright (C) 2019 webtrees development team 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 */ 17 18declare(strict_types=1); 19 20namespace Fisharebest\Webtrees; 21 22use Fisharebest\Webtrees\Functions\FunctionsImport; 23use Fisharebest\Webtrees\Services\GedcomExportService; 24use Fisharebest\Webtrees\Services\TreeService; 25use Fisharebest\Webtrees\Services\UserService; 26use InvalidArgumentException; 27 28use function stream_get_contents; 29 30/** 31 * Test harness for the class Tree 32 */ 33class TreeTest extends TestCase 34{ 35 protected static $uses_database = true; 36 37 /** 38 * @covers \Fisharebest\Webtrees\Tree::__construct 39 * @covers \Fisharebest\Webtrees\Tree::id 40 * @covers \Fisharebest\Webtrees\Tree::name 41 * @covers \Fisharebest\Webtrees\Tree::title 42 * @return void 43 */ 44 public function testConstructor(): void 45 { 46 $tree_service = new TreeService(); 47 $tree = $tree_service->create('name', 'title'); 48 49 $this->assertSame('name', $tree->name()); 50 $this->assertSame('title', $tree->title()); 51 } 52 53 /** 54 * @covers \Fisharebest\Webtrees\Tree::getPreference 55 * @covers \Fisharebest\Webtrees\Tree::setPreference 56 * @return void 57 */ 58 public function testTreePreferences(): void 59 { 60 $tree_service = new TreeService(); 61 $tree = $tree_service->create('name', 'title'); 62 63 $pref = $tree->getPreference('foo', 'default'); 64 $this->assertSame('default', $pref); 65 66 $tree->setPreference('foo', 'bar'); 67 $pref = $tree->getPreference('foo', 'default'); 68 $this->assertSame('bar', $pref); 69 } 70 71 /** 72 * @covers \Fisharebest\Webtrees\Tree::getUserPreference 73 * @covers \Fisharebest\Webtrees\Tree::setUserPreference 74 * @return void 75 */ 76 public function testUserTreePreferences(): void 77 { 78 $user_service = new UserService(); 79 $tree_service = new TreeService(); 80 $tree = $tree_service->create('name', 'title'); 81 $user = $user_service->create('user', 'User', 'user@example.com', 'secret'); 82 83 $pref = $tree->getUserPreference($user, 'foo', 'default'); 84 $this->assertSame('default', $pref); 85 86 $tree->setUserPreference($user, 'foo', 'bar'); 87 $pref = $tree->getUserPreference($user, 'foo', 'default'); 88 $this->assertSame('bar', $pref); 89 } 90 91 /** 92 * @covers \Fisharebest\Webtrees\Tree::getNewXref 93 * @return void 94 */ 95 public function testGetNewXref(): void 96 { 97 $tree_service = new TreeService(); 98 $tree = $tree_service->create('tree-name', 'Tree title'); 99 100 // New trees have an individual X1. 101 $this->assertSame('X2', $tree->getNewXref()); 102 $this->assertSame('X3', $tree->getNewXref()); 103 $this->assertSame('X4', $tree->getNewXref()); 104 $this->assertSame('X5', $tree->getNewXref()); 105 } 106 107 /** 108 * @covers \Fisharebest\Webtrees\Tree::createIndividual 109 * @return void 110 */ 111 public function testCreateInvalidIndividual(): void 112 { 113 $this->expectException(InvalidArgumentException::class); 114 115 $user_service = new UserService(); 116 $tree_service = new TreeService(); 117 $tree = $tree_service->create('name', 'title'); 118 $user = $user_service->create('user', 'User', 'user@example.com', 'secret'); 119 $user->setPreference(User::PREF_IS_ADMINISTRATOR, '1'); 120 Auth::login($user); 121 122 $tree->createIndividual("0 @@ FOO\n1 SEX U"); 123 } 124 125 /** 126 * @covers \Fisharebest\Webtrees\Tree::createIndividual 127 * @return void 128 */ 129 public function testCreateIndividual(): void 130 { 131 $user_service = new UserService(); 132 $tree_service = new TreeService(); 133 $tree = $tree_service->create('name', 'title'); 134 $user = $user_service->create('user', 'User', 'user@example.com', 'secret'); 135 $user->setPreference(User::PREF_IS_ADMINISTRATOR, '1'); 136 Auth::login($user); 137 138 $record = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/"); 139 $this->assertTrue($record->isPendingAddition()); 140 141 $user->setPreference(User::PREF_AUTO_ACCEPT_EDITS, '1'); 142 $record = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/"); 143 $this->assertFalse($record->isPendingAddition()); 144 } 145 146 /** 147 * @covers \Fisharebest\Webtrees\Tree::createFamily 148 * @return void 149 */ 150 public function testCreateInvalidFamily(): void 151 { 152 $this->expectException(InvalidArgumentException::class); 153 154 $user_service = new UserService(); 155 $tree_service = new TreeService(); 156 $tree = $tree_service->create('name', 'title'); 157 $user = $user_service->create('user', 'User', 'user@example.com', 'secret'); 158 $user->setPreference(User::PREF_IS_ADMINISTRATOR, '1'); 159 Auth::login($user); 160 161 $tree->createFamily("0 @@ FOO\n1 MARR Y"); 162 } 163 164 /** 165 * @covers \Fisharebest\Webtrees\Tree::createFamily 166 * @return void 167 */ 168 public function testCreateFamily(): void 169 { 170 $user_service = new UserService(); 171 $tree_service = new TreeService(); 172 $tree = $tree_service->create('name', 'title'); 173 $user = $user_service->create('user', 'User', 'user@example.com', 'secret'); 174 $user->setPreference(User::PREF_IS_ADMINISTRATOR, '1'); 175 Auth::login($user); 176 177 $record = $tree->createFamily("0 @@ FAM\n1 MARR Y"); 178 $this->assertTrue($record->isPendingAddition()); 179 180 $user->setPreference(User::PREF_AUTO_ACCEPT_EDITS, '1'); 181 $record = $tree->createFamily("0 @@ FAM\n1 MARR Y"); 182 $this->assertFalse($record->isPendingAddition()); 183 } 184 185 /** 186 * @covers \Fisharebest\Webtrees\Tree::createMediaObject 187 * @return void 188 */ 189 public function testCreateInvalidMediaObject(): void 190 { 191 $this->expectException(InvalidArgumentException::class); 192 193 $user_service = new UserService(); 194 $tree_service = new TreeService(); 195 $tree = $tree_service->create('name', 'title'); 196 $user = $user_service->create('user', 'User', 'user@example.com', 'secret'); 197 $user->setPreference(User::PREF_IS_ADMINISTRATOR, '1'); 198 Auth::login($user); 199 200 $tree->createMediaObject("0 @@ FOO\n1 MARR Y"); 201 } 202 203 /** 204 * @covers \Fisharebest\Webtrees\Tree::createMediaObject 205 * @return void 206 */ 207 public function testCreateMediaObject(): void 208 { 209 $user_service = new UserService(); 210 $tree_service = new TreeService(); 211 $tree = $tree_service->create('name', 'title'); 212 $user = $user_service->create('user', 'User', 'user@example.com', 'secret'); 213 $user->setPreference(User::PREF_IS_ADMINISTRATOR, '1'); 214 Auth::login($user); 215 216 $record = $tree->createMediaObject("0 @@ OBJE\n1 FILE foo.jpeg"); 217 $this->assertTrue($record->isPendingAddition()); 218 219 $user->setPreference(User::PREF_AUTO_ACCEPT_EDITS, '1'); 220 $record = $tree->createMediaObject("0 @@ OBJE\n1 FILE foo.jpeg"); 221 $this->assertFalse($record->isPendingAddition()); 222 } 223 224 /** 225 * @covers \Fisharebest\Webtrees\Tree::createRecord 226 * @return void 227 */ 228 public function testCreateInvalidRecord(): void 229 { 230 $this->expectException(InvalidArgumentException::class); 231 232 $user_service = new UserService(); 233 $tree_service = new TreeService(); 234 $tree = $tree_service->create('name', 'title'); 235 $user = $user_service->create('user', 'User', 'user@example.com', 'secret'); 236 $user->setPreference(User::PREF_IS_ADMINISTRATOR, '1'); 237 Auth::login($user); 238 239 $tree->createRecord("0 @@FOO\n1 NOTE noted"); 240 } 241 242 /** 243 * @covers \Fisharebest\Webtrees\Tree::createRecord 244 * @return void 245 */ 246 public function testCreateRecord(): void 247 { 248 $user_service = new UserService(); 249 $tree_service = new TreeService(); 250 $tree = $tree_service->create('name', 'title'); 251 $user = $user_service->create('user', 'User', 'user@example.com', 'secret'); 252 $user->setPreference(User::PREF_IS_ADMINISTRATOR, '1'); 253 Auth::login($user); 254 255 $record = $tree->createRecord("0 @@ FOO\n1 NOTE noted"); 256 $this->assertTrue($record->isPendingAddition()); 257 258 $user->setPreference(User::PREF_AUTO_ACCEPT_EDITS, '1'); 259 $record = $tree->createRecord("0 @@ FOO\n1 NOTE noted"); 260 $this->assertFalse($record->isPendingAddition()); 261 } 262 263 /** 264 * @covers \Fisharebest\Webtrees\Tree::significantIndividual 265 * @return void 266 */ 267 public function testSignificantIndividual(): void 268 { 269 $user_service = new UserService(); 270 $tree_service = new TreeService(); 271 $tree = $tree_service->create('name', 'title'); 272 $user = $user_service->create('user', 'User', 'user@example.com', 'secret'); 273 $user->setPreference(User::PREF_AUTO_ACCEPT_EDITS, '1'); 274 Auth::login($user); 275 276 // Delete the tree's default individual. 277 FunctionsImport::updateRecord('0 @X1@ INDI', $tree, true); 278 279 // No individuals in tree? Fake individual 280 $this->assertSame('I', $tree->significantIndividual($user)->xref()); 281 282 $record1 = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/"); 283 $record2 = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/"); 284 $record3 = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/"); 285 $record4 = $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/"); 286 287 // Individuals exist? First one (lowest XREF). 288 $this->assertSame($record1->xref(), $tree->significantIndividual($user)->xref()); 289 290 // Preference for tree? 291 $tree->setPreference('PEDIGREE_ROOT_ID', $record2->xref()); 292 $this->assertSame($record2->xref(), $tree->significantIndividual($user)->xref()); 293 294 // User preference 295 $tree->setUserPreference($user, User::PREF_TREE_ACCOUNT_XREF, $record3->xref()); 296 $this->assertSame($record3->xref(), $tree->significantIndividual($user)->xref()); 297 298 // User record 299 $tree->setUserPreference($user, User::PREF_TREE_DEFAULT_XREF, $record4->xref()); 300 $this->assertSame($record4->xref(), $tree->significantIndividual($user)->xref()); 301 } 302 303 /** 304 * @covers \Fisharebest\Webtrees\Tree::importGedcomFile 305 * @covers \Fisharebest\Webtrees\Tree::deleteGenealogyData 306 * @return void 307 */ 308 public function testImportAndDeleteGedcomFile(): void 309 { 310 $tree_service = new TreeService(); 311 $tree = $this->importTree('demo.ged'); 312 $this->assertNotNull($tree_service->all()->get('demo.ged')); 313 Site::setPreference('DEFAULT_GEDCOM', $tree->name()); 314 315 $tree_service->delete($tree); 316 317 $this->assertNull($tree_service->all()->get('demo.ged')); 318 $this->assertSame('', Site::getPreference('DEFAULT_GEDCOM')); 319 } 320 321 /** 322 * @covers \Fisharebest\Webtrees\Tree::hasPendingEdit 323 * @return void 324 */ 325 public function testHasPendingEdits(): void 326 { 327 $user_service = new UserService(); 328 $tree = $this->importTree('demo.ged'); 329 $user = $user_service->create('admin', 'Administrator', 'admin@example.com', 'secret'); 330 $user->setPreference(User::PREF_IS_ADMINISTRATOR, '1'); 331 Auth::login($user); 332 333 $user->setPreference(User::PREF_AUTO_ACCEPT_EDITS, '1'); 334 $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/"); 335 $this->assertFalse($tree->hasPendingEdit()); 336 337 $user->setPreference(User::PREF_AUTO_ACCEPT_EDITS, ''); 338 $tree->createIndividual("0 @@ INDI\n1 SEX F\n1 NAME Foo /Bar/"); 339 $this->assertTrue($tree->hasPendingEdit()); 340 } 341 342 /** 343 * @covers \Fisharebest\Webtrees\Services\GedcomExportService::export 344 * @return void 345 */ 346 public function testExportGedcom(): void 347 { 348 $tree = $this->importTree('demo.ged'); 349 350 $fp = fopen('php://memory', 'wb'); 351 352 $gedcom_export_service = new GedcomExportService(); 353 $gedcom_export_service->export($tree, $fp, true); 354 355 rewind($fp); 356 357 $original = file_get_contents(__DIR__ . '/../data/demo.ged'); 358 $export = stream_get_contents($fp); 359 360 // The version, date and time in the HEAD record will be different. 361 $original = preg_replace('/\n2 VERS .*/', '', $original, 1); 362 $export = preg_replace('/\n2 VERS .*/', '', $export, 1); 363 $original = preg_replace('/\n1 DATE .. ... ..../', '', $original, 1); 364 $export = preg_replace('/\n1 DATE .. ... ..../', '', $export, 1); 365 $original = preg_replace('/\n2 TIME ..:..:../', '', $original, 1); 366 $export = preg_replace('/\n2 TIME ..:..:../', '', $export, 1); 367 368 $this->assertSame($original, $export); 369 } 370} 371