1<?php
2/*
3    +-----------------------------------------------------------------------------+
4    | ILIAS open source                                                           |
5    +-----------------------------------------------------------------------------+
6    | Copyright (c) 1998-2009 ILIAS open source, University of Cologne            |
7    |                                                                             |
8    | This program is free software; you can redistribute it and/or               |
9    | modify it under the terms of the GNU General Public License                 |
10    | as published by the Free Software Foundation; either version 2              |
11    | of the License, or (at your option) any later version.                      |
12    |                                                                             |
13    | This program is distributed in the hope that it will be useful,             |
14    | but WITHOUT ANY WARRANTY; without even the implied warranty of              |
15    | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the               |
16    | GNU General Public License for more details.                                |
17    |                                                                             |
18    | You should have received a copy of the GNU General Public License           |
19    | along with this program; if not, write to the Free Software                 |
20    | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA. |
21    +-----------------------------------------------------------------------------+
22*/
23
24require_once(__DIR__ . "/mocks.php");
25
26/**
27 * TestCase for the ilObjStudyProgramme
28 * @group needsInstalledILIAS
29 *
30 * @author Michael Herren <mh@studer-raimann.ch>
31 * @author Richard Klees <richard.klees@concepts-and-training.de>
32 * @author Stefan Hecken <stefan.hecken@concepts-and-training.de>
33 * @version 1.0.0
34 */
35class ilObjStudyProgrammeTest extends PHPUnit_Framework_TestCase
36{
37    protected $backupGlobals = false;
38
39    protected function setUp()
40    {
41        PHPUnit_Framework_Error_Deprecated::$enabled = false;
42
43        require_once("./Modules/StudyProgramme/classes/class.ilObjStudyProgramme.php");
44
45        include_once("./Services/PHPUnit/classes/class.ilUnitUtil.php");
46        ilUnitUtil::performInitialisation();
47
48        $this->root_object = ilObjStudyProgramme::createInstance();
49        $this->root_object_obj_id = $this->root_object->getId();
50        $this->root_object_ref_id = $this->root_object->getRefId();
51        $this->root_object->putInTree(ROOT_FOLDER_ID);
52
53        //
54
55        global $DIC;
56        $tree = $DIC['tree'];
57        $this->tree = $tree;
58
59        global $DIC;
60        $objDefinition = $DIC['objDefinition'];
61        $this->obj_definition = $objDefinition;
62    }
63
64    protected function tearDown()
65    {
66        if ($this->root_object) {
67            $this->root_object->delete();
68        }
69    }
70
71    /**
72     * Test creation of ilObjStudyProgramme
73     */
74    public function testCreation()
75    {
76        $this->assertNotEmpty($this->root_object_obj_id);
77        $this->assertGreaterThan(0, $this->root_object_obj_id);
78
79        $this->assertNotEmpty($this->root_object_ref_id);
80        $this->assertGreaterThan(0, $this->root_object_ref_id);
81
82        $this->assertTrue($this->tree->isInTree($this->root_object_ref_id));
83    }
84
85    public function testDefaults()
86    {
87        $this->assertEquals($this->root_object->getStatus(), ilStudyProgramme::STATUS_DRAFT);
88    }
89
90    /**
91     * Test loading of ilObjStudyProgramme with obj_id. and ref_id
92     *
93     * @depends testCreation
94     */
95    public function testLoadByObjId()
96    {
97        $loaded = new ilObjStudyProgramme($this->root_object_obj_id, false);
98        $orig = $this->root_object;
99        $load_ref_id = ilObjStudyProgramme::getInstanceByRefId($this->root_object_ref_id);
100
101        $this->assertNotNull($loaded);
102        $this->assertGreaterThan(0, $loaded->getId());
103        $this->assertEquals($orig->getId(), $loaded->getId());
104        $this->assertEquals(
105            $orig->getLastChange()->get(IL_CAL_DATETIME),
106            $loaded->getLastChange()->get(IL_CAL_DATETIME)
107        );
108        $this->assertEquals($orig->getPoints(), $loaded->getPoints());
109        $this->assertEquals($orig->getLPMode(), $loaded->getLPMode());
110        $this->assertEquals($orig->getStatus(), $loaded->getStatus());
111    }
112
113    /**
114     * Test loading of ilObjStudyProgramme with ref_id.
115     *
116     * @depends testCreation
117     */
118    public function testLoadByRefId()
119    {
120        $loaded = new ilObjStudyProgramme($this->root_object_ref_id);
121        $orig = $this->root_object;
122
123        $this->assertNotNull($loaded);
124        $this->assertGreaterThan(0, $loaded->getId());
125        $this->assertEquals($orig->getId(), $loaded->getId());
126        $this->assertEquals(
127            $orig->getLastChange()->get(IL_CAL_DATETIME),
128            $loaded->getLastChange()->get(IL_CAL_DATETIME)
129        );
130        $this->assertEquals($orig->getPoints(), $loaded->getPoints());
131        $this->assertEquals($orig->getLPMode(), $loaded->getLPMode());
132        $this->assertEquals($orig->getStatus(), $loaded->getStatus());
133    }
134
135    /**
136     * Test loading over getInstance
137     *
138     * @depends testCreation
139     */
140    public function testGetInstanceByRefId()
141    {
142        require_once("Modules/StudyProgramme/classes/class.ilObjStudyProgrammeCache.php");
143
144        ilObjStudyProgrammeCache::singleton()->test_clear();
145        $this->assertTrue(ilObjStudyProgrammeCache::singleton()->test_isEmpty());
146
147        $loaded = ilObjStudyProgramme::getInstanceByRefId($this->root_object_ref_id);
148        $orig = $this->root_object;
149
150        $this->assertNotNull($loaded);
151        $this->assertGreaterThan(0, $loaded->getId());
152        $this->assertEquals($orig->getId(), $loaded->getId());
153        $this->assertEquals(
154            $orig->getLastChange()->get(IL_CAL_DATETIME),
155            $loaded->getLastChange()->get(IL_CAL_DATETIME)
156        );
157        $this->assertEquals($orig->getPoints(), $loaded->getPoints());
158        $this->assertEquals($orig->getLPMode(), $loaded->getLPMode());
159        $this->assertEquals($orig->getStatus(), $loaded->getStatus());
160    }
161
162    /**
163     * Test 	tings on ilObjStudyProgramme
164     *
165     * @depends testCreation
166     */
167    public function testSettings()
168    {
169        $obj = ilObjStudyProgramme::getInstanceByRefId($this->root_object_ref_id);
170
171        $obj->setPoints(10);
172        $obj->setStatus(ilStudyProgramme::STATUS_ACTIVE);
173        $obj->update();
174
175        $obj = ilObjStudyProgramme::getInstanceByRefId($this->root_object_ref_id);
176
177        $this->assertEquals(10, $obj->getPoints());
178        $this->assertEquals(ilStudyProgramme::STATUS_ACTIVE, $obj->getStatus());
179
180        $midnight = strtotime("today midnight");
181        $this->assertGreaterThan($midnight, $obj->getLastChange()->getUnixTime());
182    }
183
184    /**
185     * Test deletion of a ilObjStudyProgramme
186     *
187     * @depends testCreation
188     */
189    public function testDelete()
190    {
191        $deleted_object = ilObjStudyProgramme::getInstanceByRefId($this->root_object_ref_id);
192
193        $this->assertTrue($deleted_object->delete());
194    }
195
196    /**
197     * Creates a small tree, used by various tests.
198     */
199    protected function createSmallTree()
200    {
201        $first_node = ilObjStudyProgramme::createInstance();
202        $second_node = ilObjStudyProgramme::createInstance();
203        $third_node = ilObjStudyProgramme::createInstance();
204
205        $this->root_object->addNode($first_node);
206        $this->root_object->addNode($second_node);
207        $this->root_object->addNode($third_node);
208
209        $third_first_node = ilObjStudyProgramme::createInstance();
210        $third_node->addNode($third_first_node);
211    }
212
213    /**
214     * Test creating a small tree
215     *
216     * @depends testCreation
217     */
218    public function testTreeCreation()
219    {
220        $this->createSmallTree();
221        $this->assertEquals(3, $this->root_object->getAmountOfChildren());
222    }
223
224    /**
225     * Test function to get children or information about them
226     *
227     * @depends testTreeCreation
228     * @depends testGetInstanceByRefId
229     */
230    public function testTreeGetChildren()
231    {
232        $this->createSmallTree();
233
234        $children = $this->root_object->getChildren();
235        $this->assertEquals(3, count($children), "getChildren()");
236
237        $children = ilObjStudyProgramme::getAllChildren($this->root_object_ref_id);
238        $this->assertEquals(4, count($children), "ilObjStudyProgramme::getAllChildren(" . $this->root_object_ref_id . ")");
239
240        $this->assertTrue($this->root_object->hasChildren(), "hasChildren()");
241        $this->assertEquals(3, $this->root_object->getAmountOfChildren(), "getAmountOfChildren()");
242
243        $this->assertFalse($children[0]->hasChildren(), "hasChildren()");
244        $this->assertEquals(0, $children[0]->getAmountOfChildren(), "getAmountOfChildren()");
245        $this->assertEquals(0, count($children[0]->getChildren()));
246    }
247
248    /**
249     * Test getParent on ilObjStudyProgramme
250     *
251     * @depends testTreeCreation
252     */
253    public function testTreeGetParent()
254    {
255        $this->createSmallTree();
256        $children = $this->root_object->getChildren();
257
258        $child = $children[0];
259        $this->assertNotNull($child->getParent());
260        $this->assertNull($this->root_object->getParent());
261    }
262
263    /**
264     * @depends testTreeCreation
265     */
266    public function testTreeGetParents()
267    {
268        $this->createSmallTree();
269        $node3 = ilObjStudyProgramme::createInstance();
270        $children = $this->root_object->getChildren();
271        $children[0]->addNode($node3);
272
273        $parents = $node3->getParents();
274        $parent_ids = array_map(function ($node) {
275            return $node->getId();
276        }, $parents);
277        $parent_ids_expected = array( $this->root_object->getId()
278                                    , $children[0]->getId()
279                                    );
280
281        $this->assertEquals($parent_ids_expected, $parent_ids);
282    }
283
284    /**
285     * Test getDepth on ilObjStudyProgramme
286     *
287     * @depends testTreeCreation
288     */
289    public function testTreeDepth()
290    {
291        $this->createSmallTree();
292        $children = $this->root_object->getChildren();
293
294        $child = $children[0];
295
296        $this->assertEquals(1, $child->getDepth());
297    }
298
299    /**
300     * Test getRoot on ilObjStudyProgramme
301     *
302     * @depends testTreeCreation
303     */
304    public function testTreeGetRoot()
305    {
306        $this->createSmallTree();
307        $children = $this->root_object->getChildren();
308        $child = $children[0];
309
310        $this->assertEquals($this->root_object->getId(), $child->getRoot()->getId());
311    }
312
313    /**
314     * Test applyToSubTreeNodes on ilObjStudyProgramme.
315     *
316     * @depends testTreeCreation
317     */
318    public function testApplyToSubTreeNodes()
319    {
320        $this->createSmallTree();
321        $children = $this->root_object->getChildren();
322
323        $val = 0;
324        $this->root_object->applyToSubTreeNodes(function ($node) use (&$val) {
325            $val += $node->getPoints();
326        });
327
328        // We didn't make modification on the points of the nodes.
329        $this->assertEquals($val, 5 * ilStudyProgramme::DEFAULT_POINTS);
330
331
332        $this->root_object->setPoints(1);
333        $children[0]->setPoints(2);
334        $children[1]->setPoints(4);
335        $children[2]->setPoints(1);
336
337        $third_level = $children[2]->getChildren();
338        $third_level[0]->setPoints(2);
339
340        $val = 0;
341        $this->root_object->applyToSubTreeNodes(function ($node) use (&$val) {
342            $val += $node->getPoints();
343        });
344
345        $this->assertEquals($val, 10);
346    }
347
348    /**
349     * Test on addNode.
350     *
351     * @depends testTreeCreation
352     */
353    public function testAddNode()
354    {
355        $this->createSmallTree();
356
357        $children = $this->root_object->getChildren();
358        $child = $children[0];
359        $grandchild = new ilObjStudyProgramme();
360        $grandchild->create();
361        $child->addNode($grandchild);
362
363        $this->assertEquals($child->getId(), $grandchild->getParent()->getId());
364        $this->assertEquals(
365            $this->root_object->getId(),
366            $grandchild->getRoot()->getId(),
367            "Root of grandchild is root of tree."
368        );
369        $this->assertEquals(1, $child->getAmountOfChildren());
370        $this->assertEquals(2, $grandchild->getDepth());
371        $this->assertEquals($child->getLPMode(), ilStudyProgramme::MODE_POINTS);
372    }
373
374    /**
375     * Test on removeNode.
376     *
377     * @depends testTreeCreation
378     */
379    public function testRemoveNode()
380    {
381        $this->createSmallTree();
382
383        $children = $this->root_object->getChildren();
384        $child = $children[0];
385        $this->root_object->removeNode($child);
386
387        // Is not in tree anymore...
388        $raised = false;
389        try {
390            $child->getParent();
391        } catch (ilStudyProgrammeTreeException $e) {
392            $raised = true;
393        }
394        $this->assertTrue($raised, "Child does not raise on getParent after it is removed.");
395
396        $this->assertEquals(2, $this->root_object->getAmountOfChildren());
397
398        // Can't be removed a second time...
399        $raised = false;
400        try {
401            $this->root_object->removeNode($child);
402        } catch (ilStudyProgrammeTreeException $e) {
403            $raised = true;
404        }
405        $this->assertTrue($raised, "Child can be removed two times.");
406    }
407
408    /**
409     * Test on addLeaf.
410     *
411     * @depends testTreeCreation
412     */
413    public function testAddLeaf()
414    {
415        $this->createSmallTree();
416        $mock_leaf = new ilStudyProgrammeLeafMock();
417
418        $children = $this->root_object->getChildren();
419        $first_child = $children[0];
420
421        $first_child->addLeaf($mock_leaf);
422
423        // We use our mock factory, since the original factory won't know how
424        // to create our mock leaf.
425        $first_child->object_factory = new ilObjectFactoryWrapperMock();
426
427        $this->assertEquals(3, $this->root_object->getAmountOfChildren(), "getAmountOfChildren()");
428        // Check if StudyProgrammes are not counted as LP-Children
429        $this->assertEquals(0, $this->root_object->getAmountOfLPChildren(), "getAmountOfLPChildren() on root");
430        $this->assertEquals(false, $this->root_object->hasLPChildren(), "hasLPChildren() on root");
431
432        $this->assertEquals(1, $first_child->getAmountOfLPChildren(), "getAmountOfLPChildren() on first child");
433        $this->assertEquals(true, $first_child->hasLPChildren(), "hasLPChildren() on first child");
434        $this->assertEquals($first_child->getLPMode(), ilStudyProgramme::MODE_LP_COMPLETED);
435
436        $lp_children = $first_child->getLPChildren();
437        $this->assertEquals(1, count($lp_children));
438        $this->assertEquals($mock_leaf->getId(), $lp_children[0]->getId());
439    }
440
441    /**
442     * Test on removeLead.
443     *
444     * @depends testAddLeaf
445     */
446    public function testRemoveLeaf()
447    {
448        $mock_leaf = new ilStudyProgrammeLeafMock();
449        $this->root_object->addLeaf($mock_leaf);
450
451        $this->root_object->removeLeaf($mock_leaf);
452        $this->assertEquals(0, $this->root_object->getAmountOfChildren(), "getAmountOfChildren()");
453        $this->assertEquals(0, $this->root_object->getAmountOfLPChildren(), "getAmountOfLPChildren()");
454
455        $lp_children = $this->root_object->getLPChildren();
456        $this->assertEquals(0, count($lp_children));
457    }
458
459    /**
460     * Test whether nodes can only be added when there is no leaf in the
461     * parent and vice versa.
462     */
463    public function testAddWrongChildType()
464    {
465        $this->createSmallTree();
466        $children = $this->root_object->getChildren();
467        $child_n = $children[0];
468        $child_l = $children[1];
469
470        $mock_leaf1 = new ilStudyProgrammeLeafMock();
471        $mock_leaf2 = new ilStudyProgrammeLeafMock();
472        $node1 = new ilObjStudyProgramme();
473        $node2 = new ilObjStudyProgramme();
474        $node1->create();
475        $node2->create();
476
477        $child_n->addNode($node1);
478        $child_l->addLeaf($mock_leaf1);
479
480        $raised = false;
481        try {
482            $child_n->addLeaf($mock_leaf2);
483        } catch (ilStudyProgrammeTreeException $e) {
484            $raised = true;
485        }
486        $this->assertTrue($raised, "Could add leaf to program containing node.");
487
488        $raised = false;
489        try {
490            $child_n->addLeaf($mock_leaf2);
491        } catch (ilStudyProgrammeTreeException $e) {
492            $raised = true;
493        }
494        $this->assertTrue($raised, "Could add node to program containing leaf.");
495    }
496
497    /**
498     * Test on moveTo.
499     */
500    public function testMoveTo()
501    {
502        $this->createSmallTree();
503        $children = $this->root_object->getChildren();
504        $child_l = $children[0];
505        $child_r = $children[1];
506        $child_m = $children[2];
507
508        $child_r->moveTo($child_l);
509
510        $this->assertEquals(2, $child_r->getDepth());
511        $this->assertEquals($child_l->getId(), $child_r->getParent()->getId());
512        $this->assertEquals(2, $this->root_object->getAmountOfChildren());
513        $this->assertEquals(1, $child_l->getAmountOfChildren());
514
515        // test recursive moving
516        $this->assertEquals(1, $child_m->getAmountOfChildren());
517
518        $child_m->moveTo($child_r);
519
520        $m_children = $child_m->getChildren();
521        $first_third_node = $m_children[0];
522
523        $this->assertEquals(3, $child_m->getDepth());
524        $this->assertEquals(1, $child_m->getAmountOfChildren());
525        $this->assertNotNull($first_third_node);
526        $this->assertEquals(4, $first_third_node->getDepth());
527        $this->assertEquals($child_m->getId(), $first_third_node->getParent()->getId());
528
529        $this->assertEquals(1, $this->root_object->getAmountOfChildren());
530        $this->assertEquals(3, count(ilObjStudyProgramme::getAllChildren($child_l->getRefId())));
531    }
532
533    /**
534     * @expectedException ilStudyProgrammeTreeException
535     */
536    public function testCantRemoveNodeWithRelevantProgress()
537    {
538        $this->createSmallTree();
539        $children = $this->root_object->getChildren();
540        $child_l = $children[0];
541        $child_r = $children[1];
542        $this->root_object->setStatus(ilStudyProgramme::STATUS_ACTIVE);
543        $child_l->setStatus(ilStudyProgramme::STATUS_ACTIVE);
544        $child_r->setStatus(ilStudyProgramme::STATUS_ACTIVE);
545
546        $user = new ilObjUser();
547        $user->create();
548
549        $child_l->assignUser($user->getId());
550        $this->root_object->removeNode($child_l);
551    }
552
553    public function testCanRemoveNodeWithNotRelevantProgress()
554    {
555        $this->createSmallTree();
556        $children = $this->root_object->getChildren();
557        $child_l = $children[0];
558        $child_r = $children[1];
559        $this->root_object->setStatus(ilStudyProgramme::STATUS_ACTIVE);
560        $child_l->setStatus(ilStudyProgramme::STATUS_ACTIVE);
561        $child_r->setStatus(ilStudyProgramme::STATUS_OUTDATED);
562
563        $user = new ilObjUser();
564        $user->create();
565
566        $this->root_object->assignUser($user->getId());
567        $this->root_object->removeNode($child_r);
568    }
569
570    public function testCreateableSubObjects()
571    {
572        $this->createSmallTree();
573        $children = $this->root_object->getChildren();
574        $child_l = $children[0];
575
576        $all_possible_subobjects = $this->root_object->getPossibleSubObjects();
577        // don't take rolfs into account, we don't need rolf anymore
578        unset($all_possible_subobjects["rolf"]);
579
580        // this is course reference and training programme
581        $this->assertCount(2, $all_possible_subobjects);
582        $this->assertArrayHasKey("prg", $all_possible_subobjects);
583        $this->assertArrayHasKey("crsr", $all_possible_subobjects);
584
585        // root already contains program nodes, so course ref is forbidden
586        $subobjs = ilObjStudyProgramme::getCreatableSubObjects($all_possible_subobjects, $this->root_object->getRefId());
587        $this->assertCount(1, $subobjs);
588        $this->assertArrayHasKey("prg", $subobjs);
589
590        // first node contains nothing, so course ref and program node are allowed
591        $subobjs = ilObjStudyProgramme::getCreatableSubObjects($all_possible_subobjects, $child_l->getRefId());
592        $this->assertCount(2, $subobjs);
593        $this->assertArrayHasKey("prg", $subobjs);
594        $this->assertArrayHasKey("crsr", $subobjs);
595
596        $mock_leaf = new ilStudyProgrammeLeafMock();
597        $children = $this->root_object->getChildren();
598        $child_l->object_factory = new ilObjectFactoryWrapperMock();
599        $child_l->addLeaf($mock_leaf);
600
601        // Now we added a leaf, so no program nodes are allowed anymore.
602        $subobjs = ilObjStudyProgramme::getCreatableSubObjects($all_possible_subobjects, $child_l->getRefId());
603        $this->assertCount(1, $subobjs);
604        $this->assertArrayHasKey("crsr", $subobjs);
605    }
606
607    public function testCreatableSubObjectsWithoutRef()
608    {
609        $all_possible_subobjects = $this->obj_definition->getSubObjects("prg");
610        // don't take rolfs into account, we don't need rolf anymore
611        unset($all_possible_subobjects["rolf"]);
612        $this->assertEquals(
613            $all_possible_subobjects,
614            ilObjStudyProgramme::getCreatableSubObjects($all_possible_subobjects, null)
615        );
616    }
617
618    /**
619     * @expectedException ilException
620     */
621    public function testCreatableSubObjectsRaisesOnNonProgramRef()
622    {
623        ilObjStudyProgramme::getCreatableSubObjects(array(), 9);
624    }
625
626    public function testDeleteRemovesEntriesInPrgSettings()
627    {
628        $this->root_object->delete();
629        $this->root_object = null;
630
631        global $DIC;
632        $ilDB = $DIC['ilDB'];
633        $res = $ilDB->query(
634            "SELECT COUNT(*) cnt "
635                            . " FROM " . ilStudyProgramme::returnDbTableName()
636                            . " WHERE obj_id = " . $this->root_object_obj_id
637        );
638        $rec = $ilDB->fetchAssoc($res);
639        $this->assertEquals(0, $rec["cnt"]);
640    }
641
642    public function testCreatePermissionExists()
643    {
644        // Ask for permission id for creation of "foobar" to check assumption
645        // that lookupCreateOperationIds just drops unknown object types.
646        $op_ids = ilRbacReview::lookupCreateOperationIds(array("prg", "foobar"));
647        $this->assertCount(1, $op_ids);
648    }
649}
650