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