EagerLoaderTest::testMergeExternal()   A
last analyzed

Complexity

Conditions 4
Paths 4

Size

Total Lines 35

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 35
rs 9.36
c 0
b 0
f 0
cc 4
nc 4
nop 7
1
<?php
2
require_once App::pluginPath('EagerLoader') . 'Test' . DS . 'bootstrap.php';
3
4
App::uses('EagerLoader', 'EagerLoader.Model');
5
6
class EagerLoaderTest extends CakeTestCase {
7
8
/**
9
 * autoFixtures property
10
 *
11
 * @var bool
12
 */
13
	public $autoFixtures = false;
14
15
/**
16
 * Fixtures
17
 *
18
 * @var array
19
 */
20
	public $fixtures = array(
21
		'core.user',
22
		'core.article',
23
		'core.comment',
24
		'core.attachment',
25
		'core.tag',
26
		'core.articles_tag',
27
		'core.apple',
28
		'core.category',
29
	);
30
31
/**
32
 * setUp method
33
 *
34
 * @return void
35
 */
36
	public function setUp() {
37
		parent::setUp();
38
		$this->EagerLoader = new EagerLoader();
39
	}
40
41
/**
42
 * Tests reformatContain method
43
 *
44
 * @param array|string $contain Value of `contain` option
45
 * @param array $expected Expected
46
 * @return void
47
 *
48
 * @dataProvider dataProviderForTestReformatContain
49
 */
50
	public function testReformatContain($contain, $expected) {
51
		$method = new ReflectionMethod($this->EagerLoader, 'reformatContain');
52
		$method->setAccessible(true);
53
		$result = $method->invoke($this->EagerLoader, $contain);
54
		$this->assertEquals($expected, $result);
55
	}
56
57
/**
58
 * Data provider for testReformatContain
59
 *
60
 * @return array
61
 */
62
	public function dataProviderForTestReformatContain() {
63
		return array(
64
			array(
65
				// {{{ #0
66
				'User',
67
				array(
68
					'options' => array(),
69
					'contain' => array(
70
						'User' => array('options' => array(), 'contain' => array()),
71
					),
72
				),
73
				// }}}
74
			),
75
			array(
76
				// {{{ #1
77
				'User.Profile',
78
				array(
79
					'options' => array(),
80
					'contain' => array(
81
						'User' => array(
82
							'options' => array(),
83
							'contain' => array(
84
								'Profile' => array('options' => array(), 'contain' => array()),
85
							),
86
						),
87
					),
88
				),
89
				// }}}
90
			),
91
			array(
92
				// {{{ #2
93
				array(
94
					'Comment' => array(
95
						'User' => array(),
96
						'limit' => 3,
97
						'order' => array('id' => 'desc'),
98
						'conditions' => array('published' => 'Y'),
99
					),
100
				),
101
				array(
102
					'options' => array(),
103
					'contain' => array(
104
						'Comment' => array(
105
							'options' => array(
106
								'limit' => 3,
107
								'order' => array('id' => 'desc'),
108
								'conditions' => array('published' => 'Y'),
109
							),
110
							'contain' => array(
111
								'User' => array('options' => array(), 'contain' => array()),
112
							),
113
						),
114
					),
115
				),
116
				// }}}
117
			),
118
			array(
119
				// {{{ #3
120
				array(
121
					'User' => array('fields' => array('name')),
122
					'User.Profile' => array('fields' => array('address')),
123
					'Comment.User.Profile',
124
				),
125
				array(
126
					'options' => array(),
127
					'contain' => array(
128
						'User' => array(
129
							'contain' => array(
130
								'Profile' => array('options' => array('fields' => array('address')), 'contain' => array()),
131
							),
132
							'options' => array('fields' => array('name')),
133
						),
134
						'Comment' => array(
135
							'options' => array(),
136
							'contain' => array(
137
								'User' => array(
138
									'options' => array(),
139
									'contain' => array(
140
										'Profile' => array('options' => array(), 'contain' => array()),
141
									),
142
								),
143
							),
144
						),
145
					),
146
				),
147
				// }}}
148
			),
149
		);
150
	}
151
152
/**
153
 * Tests buildJoinQuery method
154
 *
155
 * @return void
156
 */
157
	public function testBuildJoinQuery() {
158
		$this->loadFixtures('User');
159
		$User = ClassRegistry::init('User');
160
161
		$db = $User->getDataSource();
0 ignored issues
show
Unused Code introduced by
$db is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
162
163
		$method = new ReflectionMethod($this->EagerLoader, 'buildJoinQuery');
164
		$method->setAccessible(true);
165
		$result = $method->invokeArgs($this->EagerLoader, array(
166
			$User,
167
			array('fields' => array()),
168
			'INNER',
169
			array('Article.user_id' => 'User.id'),
170
			array(
171
				'conditions' => array(
172
					array('Article.user_id' => array(1, 2, 3)),
173
				)
174
			)
175
		));
176
177
		$expected = array(
178
			// {{{
179
			'fields' => array(
180
				'User.id',
181
				'User.user',
182
				'User.password',
183
				'User.created',
184
				'User.updated',
185
				'Article.user_id',
186
			),
187
			'joins' => array(
188
				array(
189
					'type' => 'INNER',
190
					'table' => $User,
191
					'alias' => 'User',
192
					'conditions' => array(
193
						array('Article.user_id' => array(1, 2, 3)),
194
						array('Article.user_id' => (object)array('type' => 'identifier', 'value' => 'User.id')),
195
					),
196
				),
197
			),
198
			'conditions' => array(),
199
			'order' => array(),
200
			// }}}
201
		);
202
203
		$this->assertEquals($expected, $result);
204
	}
205
206
/**
207
 * Tests perseContain method
208
 *
209
 * @param string $model Parent model of the contained model
210
 * @param string $alias Alias of the contained model
211
 * @param array $contain Reformatted `contain` option for the deep associations
212
 * @param array $expected Expected
213
 * @return void
214
 *
215
 * @dataProvider dataProviderForTestParseContain
216
 */
217
	public function testParseContain($model, $alias, $contain, $expected) {
218
		$this->loadFixtures('ArticlesTag', 'Apple');
219
220
		$model = ClassRegistry::init($model);
221
222
		$method = new ReflectionMethod($this->EagerLoader, 'parseContain');
223
		$method->setAccessible(true);
224
225
		$result = $method->invokeArgs($this->EagerLoader, array($model, $alias, $contain));
226
227
		// Remove something
228
		$result = Hash::remove($result, '{s}.{n}.target');
229
		$result = Hash::remove($result, '{s}.{n}.parent');
230
		$result = Hash::remove($result, '{s}.{n}.habtm');
231
		$result = Hash::remove($result, '{s}.{n}.finderQuery');
232
233
		$this->assertEquals($expected, $result);
234
	}
235
236
/**
237
 * Data provider for testParseContain
238
 *
239
 * @return array
240
 */
241
	public function dataProviderForTestParseContain() {
242
		return array(
243
			array(
244
				// {{{ #0 normal
245
				'Comment',
246
				'Article',
247
				array(
248
					'options' => array(),
249
					'contain' => array(
250
						'User' => array(
251
							'options' => array(),
252
							'contain' => array(),
253
						),
254
					),
255
				),
256
				array(
257
					'Comment' => array(
258
						array(
259
							'alias' => 'Article',
260
							'parentAlias' => 'Comment',
261
							'parentKey' => 'article_id',
262
							'targetKey' => 'id',
263
							'aliasPath' => 'Comment.Article',
264
							'propertyPath' => 'Article',
265
							'options' => array(),
266
							'has' => false,
267
							'belong' => true,
268
							'many' => false,
269
							'external' => false,
270
						),
271
						array(
272
							'alias' => 'User',
273
							'parentAlias' => 'Article',
274
							'parentKey' => 'user_id',
275
							'targetKey' => 'id',
276
							'aliasPath' => 'Comment.Article.User',
277
							'propertyPath' => 'Article.User',
278
							'options' => array(),
279
							'has' => false,
280
							'belong' => true,
281
							'many' => false,
282
							'external' => false,
283
							'eager' => true,
284
						),
285
					),
286
				),
287
				// }}}
288
			),
289
			array(
290
				// {{{ #1 complex
291
				'User',
292
				'Article',
293
				array(
294
					'options' => array(),
295
					'contain' => array(
296
						'Comment' => array(
297
							'options' => array('limit' => 3),
298
							'contain' => array(
299
								'User' => array('options' => array(), 'contain' => array()),
300
								'Attachment' => array('options' => array(), 'contain' => array()),
301
							),
302
						),
303
						'Tag' => array('options' => array(), 'contain' => array())
304
					),
305
				),
306
				array(
307
					'User' => array(
308
						array(
309
							'alias' => 'Article',
310
							'parentAlias' => 'User',
311
							'parentKey' => 'id',
312
							'targetKey' => 'user_id',
313
							'aliasPath' => 'User.Article',
314
							'propertyPath' => 'Article',
315
							'options' => array(),
316
							'has' => true,
317
							'belong' => false,
318
							'many' => true,
319
							'external' => true,
320
						),
321
					),
322
					'User.Article' => array(
323
						array(
324
							'alias' => 'Comment',
325
							'parentAlias' => 'Article',
326
							'parentKey' => 'id',
327
							'targetKey' => 'article_id',
328
							'aliasPath' => 'User.Article.Comment',
329
							'propertyPath' => 'Article.Comment',
330
							'options' => array('limit' => 3),
331
							'has' => true,
332
							'belong' => false,
333
							'many' => true,
334
							'external' => true,
335
						),
336
						array(
337
							'alias' => 'Tag',
338
							'parentAlias' => 'Article',
339
							'parentKey' => 'id',
340
							'targetKey' => 'id',
341
							'aliasPath' => 'User.Article.Tag',
342
							'propertyPath' => 'Article.Tag',
343
							'options' => array(),
344
							'has' => true,
345
							'belong' => true,
346
							'many' => true,
347
							'habtmAlias' => 'ArticlesTag',
348
							'habtmParentKey' => 'article_id',
349
							'habtmTargetKey' => 'tag_id',
350
							'external' => true,
351
						),
352
					),
353
					'User.Article.Comment' => array(
354
						array(
355
							'alias' => 'User',
356
							'parentAlias' => 'Comment',
357
							'parentKey' => 'user_id',
358
							'targetKey' => 'id',
359
							'aliasPath' => 'User.Article.Comment.User',
360
							'propertyPath' => 'Comment.User',
361
							'options' => array(),
362
							'has' => false,
363
							'belong' => true,
364
							'many' => false,
365
							'external' => false,
366
						),
367
						array(
368
							'alias' => 'Attachment',
369
							'parentAlias' => 'Comment',
370
							'parentKey' => 'id',
371
							'targetKey' => 'comment_id',
372
							'aliasPath' => 'User.Article.Comment.Attachment',
373
							'propertyPath' => 'Comment.Attachment',
374
							'options' => array(),
375
							'has' => true,
376
							'belong' => false,
377
							'many' => false,
378
							'external' => false,
379
						),
380
					),
381
				),
382
				// }}}
383
			),
384
			array(
385
				// {{{ #2 HABTM
386
				'Article',
387
				'Tag',
388
				array(
389
					'options' => array(),
390
					'contain' => array(
391
						'Article' => array('options' => array(), 'contain' => array()),
392
					),
393
				),
394
				array(
395
					'Article' => array(
396
						array(
397
							'alias' => 'Tag',
398
							'parentAlias' => 'Article',
399
							'parentKey' => 'id',
400
							'targetKey' => 'id',
401
							'aliasPath' => 'Article.Tag',
402
							'propertyPath' => 'Tag',
403
							'options' => array(),
404
							'has' => true,
405
							'belong' => true,
406
							'many' => true,
407
							'habtmAlias' => 'ArticlesTag',
408
							'habtmParentKey' => 'article_id',
409
							'habtmTargetKey' => 'tag_id',
410
							'external' => true,
411
						),
412
					),
413
					'Article.Tag' => array(
414
						array(
415
							'alias' => 'Article',
416
							'parentAlias' => 'Tag',
417
							'parentKey' => 'id',
418
							'targetKey' => 'id',
419
							'aliasPath' => 'Article.Tag.Article',
420
							'propertyPath' => 'Tag.Article',
421
							'options' => array(),
422
							'has' => true,
423
							'belong' => true,
424
							'many' => true,
425
							'habtmAlias' => 'ArticlesTag',
426
							'habtmParentKey' => 'tag_id',
427
							'habtmTargetKey' => 'article_id',
428
							'external' => true,
429
						),
430
					),
431
				),
432
				// }}}
433
			),
434
			array(
435
				// {{{ #3 association (external)
436
				'Article',
437
				'SecondComment',
438
				array('options' => array(), 'contain' => array()),
439
				array(
440
					'Article' => array(
441
						array(
442
							'alias' => 'SecondComment',
443
							'parentAlias' => 'Article',
444
							'parentKey' => 'id',
445
							'targetKey' => 'article_id',
446
							'aliasPath' => 'Article.SecondComment',
447
							'propertyPath' => 'SecondComment',
448
							'options' => array(
449
								'order' => 'SecondComment.id',
450
								'limit' => 1,
451
								'offset' => 1,
452
							),
453
							'has' => true,
454
							'belong' => false,
455
							'many' => false,
456
							'external' => true,
457
						),
458
					),
459
				),
460
				// }}}
461
			),
462
			array(
463
				// {{{ #4 association (finderQuery)
464
				'Apple',
465
				'NextApple',
466
				array(
467
					'options' => array(),
468
					'contain' => array(
469
						'ParentApple' => array('options' => array(), 'contain' => array()),
470
					)
471
				),
472
				array(
473
					'Apple' => array(
474
						array(
475
							'alias' => 'NextApple',
476
							'parentAlias' => 'Apple',
477
							'parentKey' => 'id',
478
							'targetKey' => 'apple_id',
479
							'aliasPath' => 'Apple.NextApple',
480
							'propertyPath' => 'NextApple',
481
							'options' => array(),
482
							'has' => true,
483
							'belong' => false,
484
							'many' => false,
485
							'external' => true,
486
						),
487
					),
488
					'Apple.NextApple' => array(
489
						array(
490
							'alias' => 'ParentApple',
491
							'parentAlias' => 'NextApple',
492
							'parentKey' => 'apple_id',
493
							'targetKey' => 'id',
494
							'aliasPath' => 'Apple.NextApple.ParentApple',
495
							'propertyPath' => 'NextApple.ParentApple',
496
							'options' => array(),
497
							'has' => false,
498
							'belong' => true,
499
							'many' => false,
500
							'external' => true,
501
						),
502
					),
503
				),
504
				// }}}
505
			),
506
			array(
507
				// {{{ #5 duplication
508
				'Attachment',
509
				'Comment',
510
				array(
511
					'options' => array(),
512
					'contain' => array(
513
						'Article' => array(
514
							'options' => array(),
515
							'contain' => array(
516
								'User' => array('options' => array(), 'contain' => array()),
517
							),
518
						),
519
						'User' => array('options' => array(), 'contain' => array()),
520
					)
521
				),
522
				array(
523
					'Attachment' => array(
524
						array(
525
							'alias' => 'Comment',
526
							'parentAlias' => 'Attachment',
527
							'parentKey' => 'comment_id',
528
							'targetKey' => 'id',
529
							'aliasPath' => 'Attachment.Comment',
530
							'propertyPath' => 'Comment',
531
							'options' => array(),
532
							'has' => false,
533
							'belong' => true,
534
							'many' => false,
535
							'external' => false,
536
						),
537
						array(
538
							'alias' => 'Article',
539
							'parentAlias' => 'Comment',
540
							'parentKey' => 'article_id',
541
							'targetKey' => 'id',
542
							'aliasPath' => 'Attachment.Comment.Article',
543
							'propertyPath' => 'Comment.Article',
544
							'options' => array(),
545
							'has' => false,
546
							'belong' => true,
547
							'many' => false,
548
							'external' => false,
549
							'eager' => true,
550
						),
551
						array(
552
							'alias' => 'User',
553
							'parentAlias' => 'Article',
554
							'parentKey' => 'user_id',
555
							'targetKey' => 'id',
556
							'aliasPath' => 'Attachment.Comment.Article.User',
557
							'propertyPath' => 'Comment.Article.User',
558
							'options' => array(),
559
							'has' => false,
560
							'belong' => true,
561
							'many' => false,
562
							'external' => false,
563
							'eager' => true,
564
						),
565
					),
566
					'Attachment.Comment' => array(
567
						array(
568
							'alias' => 'User',
569
							'parentAlias' => 'Comment',
570
							'parentKey' => 'user_id',
571
							'targetKey' => 'id',
572
							'aliasPath' => 'Attachment.Comment.User',
573
							'propertyPath' => 'Comment.User',
574
							'options' => array(),
575
							'has' => false,
576
							'belong' => true,
577
							'many' => false,
578
							'external' => true,
579
						),
580
					),
581
				),
582
				// }}}
583
			),
584
			array(
585
				// {{{ #6 self
586
				'Apple',
587
				'ParentApple',
588
				array(
589
					'options' => array(),
590
					'contain' => array(
591
						'ParentApple' => array('options' => array(), 'contain' => array()),
592
					)
593
				),
594
				array(
595
					'Apple' => array(
596
						array(
597
							'alias' => 'ParentApple',
598
							'parentAlias' => 'Apple',
599
							'parentKey' => 'apple_id',
600
							'targetKey' => 'id',
601
							'aliasPath' => 'Apple.ParentApple',
602
							'propertyPath' => 'ParentApple',
603
							'options' => array(),
604
							'has' => false,
605
							'belong' => true,
606
							'many' => false,
607
							'external' => false,
608
						),
609
					),
610
					'Apple.ParentApple' => array(
611
						array(
612
							'alias' => 'ParentApple',
613
							'parentAlias' => 'ParentApple',
614
							'parentKey' => 'apple_id',
615
							'targetKey' => 'id',
616
							'aliasPath' => 'Apple.ParentApple.ParentApple',
617
							'propertyPath' => 'ParentApple.ParentApple',
618
							'options' => array(),
619
							'has' => false,
620
							'belong' => true,
621
							'many' => false,
622
							'external' => true,
623
						),
624
					),
625
				),
626
				// }}}
627
			),
628
629
		);
630
	}
631
632
/**
633
 * Tests that parseContain method throws an exception,
634
 * if the parent model is not associated with the specified model.
635
 *
636
 * @return void
637
 *
638
 * @expectedException InvalidArgumentException
639
 * @expectedExceptionMessage Model "User" is not associated with model "Something"
640
 */
641
	public function testParseContainThrowsException() {
642
		$User = ClassRegistry::init('User');
643
644
		$method = new ReflectionMethod($this->EagerLoader, 'parseContain');
645
		$method->setAccessible(true);
646
		$method->invokeArgs($this->EagerLoader, array(
647
			$User,
648
			'Something',
649
			array('options' => array(), 'contain' => array()),
650
			array(
651
				'root' => 'User',
652
				'aliasPath' => 'User',
653
				'propertyPath' => '',
654
			)
655
		));
656
	}
657
658
/**
659
 * Tests normalizeQuery method
660
 *
661
 * @param string $model Model
662
 * @param array $query Query
663
 * @param array $expected Expected
664
 * @return void
665
 *
666
 * @dataProvider dataProviderForTestNormalizeQuery
667
 */
668
	public function testNormalizeQuery($model, $query, $expected) {
669
		$this->loadFixtures($model);
670
671
		$model = ClassRegistry::init($model);
672
673
		$db = $model->getDataSource();
674
		$startQuote = $db->startQuote;
675
		$endQuote = $db->endQuote;
676
		$db->startQuote = '';
677
		$db->endQuote = '';
678
679
		$method = new ReflectionMethod($this->EagerLoader, 'normalizeQuery');
680
		$method->setAccessible(true);
681
		$result = $method->invokeArgs($this->EagerLoader, array($model, $query));
682
683
		$db->startQuote = $startQuote;
684
		$db->endQuote = $endQuote;
685
686
		$this->assertEquals($expected, $result);
687
	}
688
689
/**
690
 * Data provider for testNormalizeQuery
691
 *
692
 * @return array
693
 */
694
	public function dataProviderForTestNormalizeQuery() {
695
		return array(
696
			array(
697
				// {{{ #0
698
				'User',
699
				array(),
700
				array(
701
					'fields' => array(
702
						'User.id',
703
						'User.user',
704
						'User.password',
705
						'User.created',
706
						'User.updated',
707
					),
708
					'conditions' => array(),
709
					'order' => array(),
710
				),
711
				// }}}
712
			),
713
			array(
714
				// {{{ #1
715
				'User',
716
				array(
717
					'fields' => 'User.id',
718
					'conditions' => '1 = 1',
719
				),
720
				array(
721
					'fields' => array('User.id'),
722
					'conditions' => array('1 = 1'),
723
					'order' => array(),
724
				),
725
				// }}}
726
			),
727
			array(
728
				// {{{ #2
729
				'User',
730
				array(
731
					'fields' => array(
732
						'User.id',
733
						'user',
734
						'password',
735
					),
736
					'conditions' => array(
737
						'user' => 'larry',
738
					),
739
				),
740
				array(
741
					'fields' => array(
742
						'User.id',
743
						'User.user',
744
						'User.password',
745
					),
746
					'conditions' => array(
747
						array('User.user' => 'larry'),
748
					),
749
					'order' => array(),
750
				),
751
				// }}}
752
			),
753
			array(
754
				// {{{ #3
755
				'User',
756
				array(
757
					'fields' => 'id',
758
					'conditions' => array(),
759
					'order' => array(
760
						'id' => 'ASC',
761
						'User.id' => 'DESC',
762
						'created' => 'DESC',
763
						'updated',
764
					),
765
				),
766
				array(
767
					'fields' => array(
768
						'User.id',
769
					),
770
					'conditions' => array(),
771
					'order' => array(
772
						'User.id' => 'ASC',
773
						'User.created' => 'DESC',
774
						'User.updated',
775
					),
776
				),
777
				// }}}
778
			),
779
			array(
780
				// {{{ #4 Virtual fields
781
				'Category',
782
				array(
783
					'fields' => 'is_root',
784
					'conditions' => array('is_root' => 0),
785
					'order' => array(
786
						'is_root',
787
					),
788
				),
789
				array(
790
					'fields' => array(
791
						'(CASE WHEN Category.parent_id = 0 THEN 1 ELSE 0 END) AS  Category__is_root',
792
					),
793
					'conditions' => array(
794
						(object)array(
795
							'type' => 'expression',
796
							'value' => '(CASE WHEN Category.parent_id = 0 THEN 1 ELSE 0 END) = 0',
797
						)
798
					),
799
					'order' => array(
800
						'(CASE WHEN Category.parent_id = 0 THEN 1 ELSE 0 END)',
801
					),
802
				),
803
				// }}}
804
			),
805
		);
806
	}
807
808
/**
809
 * Tests mergeExternalExternal and mergeInternalExternal
810
 *
811
 * @param string $parent Name of the parent model
812
 * @param string $target Name of the target model
813
 * @param array $meta Meta data to be used for eager loading
814
 * @param array $results Results
815
 * @param array $fixtures Fixtures to be used
816
 * @param array $expectedArgument Expected argument for loadExternal method
817
 * @param array $expectedResults Expected results
818
 * @return void
819
 *
820
 * @dataProvider dataProviderForTestMergeExternal
821
 */
822
	public function testMergeExternal($parent, $target, $meta, $results, $fixtures, $expectedArgument, $expectedResults) {
823
		call_user_func_array(array($this, 'loadFixtures'), $fixtures);
824
825
		$parent = ClassRegistry::init($parent);
826
		$target = $parent->$target;
827
828
		$meta += array(
829
			'parent' => $parent,
830
			'target' => $target,
831
			'alias' => $target->alias,
832
			'parentAlias' => $parent->alias,
833
			'aliasPath' => $parent->alias . '.' . $target->alias,
834
			'propertyPath' => $parent->alias . '.' . $target->alias,
835
		);
836
837
		if (isset($meta['habtmAlias'])) {
838
			$meta['habtm'] = $parent->{$meta['habtmAlias']};
839
		}
840
841
		if ($target->alias === 'NextApple') {
842
			$meta['finderQuery'] = $target->getNextAppleFinderQuery();
843
		}
844
845
		$EagerLoader = $this->getMock('EagerLoader', array('loadExternal'));
846
		$EagerLoader->expects($this->once())
847
			->method('loadExternal')
848
			->with($target, $meta['aliasPath'], $expectedArgument)
849
			->will($this->returnArgument(2));
850
851
		$method = new ReflectionMethod($EagerLoader, ($meta['external'] ? 'mergeExternalExternal' : 'mergeInternalExternal'));
852
		$method->setAccessible(true);
853
		$merged = $method->invokeArgs($EagerLoader, array($target, $results, $meta));
854
855
		$this->assertEquals($expectedResults, $merged);
856
	}
857
858
/**
859
 * Data provider for testMergeExternal method
860
 *
861
 * @return array
862
 */
863
	public function dataProviderForTestMergeExternal() {
864
		return array(
865
			array(
866
				// {{{ #0 hasMany
867
				'User',
868
				'Comment',
869
				// $meta
870
				array(
871
					'parentKey' => 'id',
872
					'targetKey' => 'user_id',
873
					'options' => array('fields' => 'id'),
874
					'has' => true,
875
					'belong' => false,
876
					'many' => true,
877
					'external' => true,
878
				),
879
				// $results
880
				array(
881
					array(
882
						'User' => array(
883
							'id' => '2',
884
						),
885
					),
886
					array(
887
						'User' => array(
888
							'id' => '4',
889
						),
890
					),
891
				),
892
				// $frixtures
893
				array('User', 'Comment'),
894
				// $expectedArgument
895
				array(
896
					array(
897
						'Comment' => array(
898
							'id' => '1',
899
							'user_id' => '2',
900
						),
901
						'EagerLoaderModel' => array(
902
							'assoc_id' => '2',
903
						),
904
					),
905
					array(
906
						'Comment' => array(
907
							'id' => '2',
908
							'user_id' => '4',
909
						),
910
						'EagerLoaderModel' => array(
911
							'assoc_id' => '4',
912
						),
913
					),
914
					array(
915
						'Comment' => array(
916
							'id' => '6',
917
							'user_id' => '2',
918
						),
919
						'EagerLoaderModel' => array(
920
							'assoc_id' => '2',
921
						),
922
					),
923
				),
924
				// $expectedResults
925
				array(
926
					array(
927
						'User' => array(
928
							'id' => '2',
929
							'Comment' => array(
930
								array(
931
									'id' => '1',
932
									'user_id' => '2',
933
								),
934
								array(
935
									'id' => '6',
936
									'user_id' => '2',
937
								),
938
							),
939
						),
940
					),
941
					array(
942
						'User' => array(
943
							'id' => '4',
944
							'Comment' => array(
945
								array(
946
									'id' => '2',
947
									'user_id' => '4',
948
								),
949
							),
950
						),
951
					)
952
				),
953
				// }}}
954
			),
955
			array(
956
				// {{{ #1 hasMany (limited)
957
				'User',
958
				'Comment',
959
				// $meta
960
				array(
961
					'parentKey' => 'id',
962
					'targetKey' => 'user_id',
963
					'options' => array('fields' => 'id', 'limit' => 1),
964
					'has' => true,
965
					'belong' => false,
966
					'many' => true,
967
					'external' => true,
968
				),
969
				// $results
970
				array(
971
					array(
972
						'User' => array(
973
							'id' => '2',
974
						),
975
					),
976
					array(
977
						'User' => array(
978
							'id' => '4',
979
						),
980
					),
981
				),
982
				// $frixtures
983
				array('User', 'Comment'),
984
				// $expectedArgument
985
				array(
986
					array(
987
						'Comment' => array(
988
							'id' => '1',
989
							'user_id' => '2',
990
						),
991
						'EagerLoaderModel' => array(
992
							'assoc_id' => '2',
993
						),
994
					),
995
					array(
996
						'Comment' => array(
997
							'id' => '2',
998
							'user_id' => '4',
999
						),
1000
						'EagerLoaderModel' => array(
1001
							'assoc_id' => '4',
1002
						),
1003
					),
1004
				),
1005
				// $expectedResults
1006
				array(
1007
					array(
1008
						'User' => array(
1009
							'id' => '2',
1010
							'Comment' => array(
1011
								array(
1012
									'id' => '1',
1013
									'user_id' => '2',
1014
								),
1015
							),
1016
						),
1017
					),
1018
					array(
1019
						'User' => array(
1020
							'id' => '4',
1021
							'Comment' => array(
1022
								array(
1023
									'id' => '2',
1024
									'user_id' => '4',
1025
								),
1026
							),
1027
						),
1028
					)
1029
				),
1030
				// }}}
1031
			),
1032
			array(
1033
				// {{{ #2 hasOne (external)
1034
				'Comment',
1035
				'Attachment',
1036
				// $meta
1037
				array(
1038
					'parentKey' => 'id',
1039
					'targetKey' => 'comment_id',
1040
					'options' => array('fields' => 'id'),
1041
					'has' => true,
1042
					'belong' => false,
1043
					'many' => false,
1044
					'external' => true,
1045
				),
1046
				// $results
1047
				array(
1048
					array(
1049
						'Comment' => array(
1050
							'id' => '5',
1051
						),
1052
					),
1053
				),
1054
				// $frixtures
1055
				array('Comment', 'Attachment'),
1056
				// $expectedArgument
1057
				array(
1058
					array(
1059
						'Attachment' => array(
1060
							'id' => '1',
1061
							'comment_id' => '5',
1062
						),
1063
						'EagerLoaderModel' => array(
1064
							'assoc_id' => '5',
1065
						),
1066
					),
1067
				),
1068
				// $expectedResults
1069
				array(
1070
					array(
1071
						'Comment' => array(
1072
							'id' => '5',
1073
							'Attachment' => array(
1074
								'id' => '1',
1075
								'comment_id' => '5',
1076
							),
1077
						),
1078
					),
1079
				),
1080
				// }}}
1081
			),
1082
			array(
1083
				// {{{ #3 hasAndBelongsToMany
1084
				'Article',
1085
				'Tag',
1086
				// $meta
1087
				array(
1088
					'parentKey' => 'id',
1089
					'targetKey' => 'id',
1090
					'options' => array('fields' => 'id', 'order' => array('ArticlesTag.article_id')),
1091
					'habtmAlias' => 'ArticlesTag',
1092
					'habtmParentKey' => 'article_id',
1093
					'habtmTargetKey' => 'tag_id',
1094
					'has' => true,
1095
					'belong' => true,
1096
					'many' => true,
1097
					'external' => true,
1098
				),
1099
				// $results
1100
				array(
1101
					array(
1102
						'Article' => array(
1103
							'id' => '1',
1104
						),
1105
					),
1106
					array(
1107
						'Article' => array(
1108
							'id' => '2',
1109
						),
1110
					),
1111
				),
1112
				// $frixtures
1113
				array('Article', 'Tag', 'ArticlesTag'),
1114
				// $expectedArgument
1115
				array(
1116
					array(
1117
						'Tag' => array(
1118
							'id' => '1',
1119
						),
1120
						'ArticlesTag' => array(
1121
							'article_id' => '1',
1122
							'tag_id' => '1',
1123
						),
1124
						'EagerLoaderModel' => array(
1125
							'assoc_id' => '1',
1126
						),
1127
					),
1128
					array(
1129
						'Tag' => array(
1130
							'id' => '2',
1131
						),
1132
						'ArticlesTag' => array(
1133
							'article_id' => '1',
1134
							'tag_id' => '2',
1135
						),
1136
						'EagerLoaderModel' => array(
1137
							'assoc_id' => '1',
1138
						),
1139
					),
1140
					array(
1141
						'Tag' => array(
1142
							'id' => '1',
1143
						),
1144
						'ArticlesTag' => array(
1145
							'article_id' => '2',
1146
							'tag_id' => '1',
1147
						),
1148
						'EagerLoaderModel' => array(
1149
							'assoc_id' => '2',
1150
						),
1151
					),
1152
					array(
1153
						'Tag' => array(
1154
							'id' => '3',
1155
						),
1156
						'ArticlesTag' => array(
1157
							'article_id' => '2',
1158
							'tag_id' => '3',
1159
						),
1160
						'EagerLoaderModel' => array(
1161
							'assoc_id' => '2',
1162
						),
1163
					),
1164
				),
1165
				// $expectedResults
1166
				array(
1167
					array(
1168
						'Article' => array(
1169
							'id' => '1',
1170
							'Tag' => array(
1171
								array(
1172
									'id' => '1',
1173
									'ArticlesTag' => array(
1174
										'article_id' => '1',
1175
										'tag_id' => '1',
1176
									),
1177
								),
1178
								array(
1179
									'id' => '2',
1180
									'ArticlesTag' => array(
1181
										'article_id' => '1',
1182
										'tag_id' => '2',
1183
									),
1184
								),
1185
							),
1186
						),
1187
					),
1188
					array(
1189
						'Article' => array(
1190
							'id' => '2',
1191
							'Tag' => array(
1192
								array(
1193
									'id' => '1',
1194
									'ArticlesTag' => array(
1195
										'article_id' => '2',
1196
										'tag_id' => '1',
1197
									),
1198
								),
1199
								array(
1200
									'id' => '3',
1201
									'ArticlesTag' => array(
1202
										'article_id' => '2',
1203
										'tag_id' => '3',
1204
									),
1205
								),
1206
							),
1207
						),
1208
					),
1209
				),
1210
				// }}}
1211
			),
1212
			array(
1213
				// {{{ #4 hasOne (finderQuery)
1214
				'Apple',
1215
				'NextApple',
1216
				// $meta
1217
				array(
1218
					'parentAlias' => 'Apple',
1219
					'parentKey' => 'id',
1220
					'targetKey' => 'apple_id',
1221
					'aliasPath' => 'Apple.NextApple',
1222
					'propertyPath' => 'Apple.NextApple',
1223
					'options' => array(),
1224
					'has' => true,
1225
					'belong' => false,
1226
					'many' => false,
1227
					'external' => true,
1228
				),
1229
				// $results
1230
				array(
1231
					array(
1232
						'Apple' => array(
1233
							'id' => '1',
1234
						),
1235
					),
1236
					array(
1237
						'Apple' => array(
1238
							'id' => '5',
1239
						),
1240
					),
1241
				),
1242
				// $fixtures
1243
				array('Apple'),
1244
				// $expectedArgument
1245
				array(
1246
					array(
1247
						'NextApple' => array(
1248
							'id' => '2',
1249
							'apple_id' => '1',
1250
							'color' => 'Bright Red 1',
1251
							'name' => 'Bright Red Apple',
1252
							'created' => '2006-11-22 10:43:13',
1253
							'modified' => '2006-11-30 18:38:10',
1254
						),
1255
						'EagerLoaderModel' => array(
1256
							'assoc_id' => '1',
1257
						),
1258
					),
1259
					array(
1260
						'NextApple' => array(
1261
							'id' => '6',
1262
							'apple_id' => 4,
1263
							'color' => 'My new appleOrange',
1264
							'name' => 'My new apple',
1265
							'created' => '2006-12-25 05:29:39',
1266
							'modified' => '2006-12-25 05:29:39',
1267
						),
1268
						'EagerLoaderModel' => array(
1269
							'assoc_id' => '5',
1270
						),
1271
					),
1272
				),
1273
				// $expectedResults
1274
				array(
1275
					array(
1276
						'Apple' => array(
1277
							'id' => '1',
1278
							'NextApple' => array(
1279
								'id' => '2',
1280
								'apple_id' => 1,
1281
								'color' => 'Bright Red 1',
1282
								'name' => 'Bright Red Apple',
1283
								'created' => '2006-11-22 10:43:13',
1284
								'modified' => '2006-11-30 18:38:10',
1285
							),
1286
						),
1287
					),
1288
					array(
1289
						'Apple' => array(
1290
							'id' => '5',
1291
							'NextApple' => array(
1292
								'id' => '6',
1293
								'apple_id' => 4,
1294
								'color' => 'My new appleOrange',
1295
								'name' => 'My new apple',
1296
								'created' => '2006-12-25 05:29:39',
1297
								'modified' => '2006-12-25 05:29:39',
1298
							),
1299
						),
1300
					),
1301
				),
1302
				// }}}
1303
			),
1304
			array(
1305
				// {{{ #5 belongsTo
1306
				'Article',
1307
				'User',
1308
				// $meta
1309
				array(
1310
					'parentAlias' => 'Article',
1311
					'parentKey' => 'user_id',
1312
					'targetKey' => 'id',
1313
					'aliasPath' => 'Article.User',
1314
					'propertyPath' => 'Article.User',
1315
					'options' => array(
1316
						'fields' => 'id',
1317
					),
1318
					'has' => false,
1319
					'belong' => true,
1320
					'many' => false,
1321
					'external' => false,
1322
				),
1323
				// $results
1324
				array(
1325
					array(
1326
						'Article' => array(
1327
							'id' => '1',
1328
							'user_id' => '1',
1329
						),
1330
						'User' => array(
1331
							'id' => '1',
1332
							'dummy' => '1',
1333
						),
1334
					),
1335
					array(
1336
						'Article' => array(
1337
							'id' => '3',
1338
							'user_id' => '1',
1339
						),
1340
						'User' => array(
1341
							'id' => '1',
1342
							'dummy' => '2',
1343
						),
1344
					)
1345
				),
1346
				// $fixtures
1347
				array('Article', 'User'),
1348
				// $expectedArgument
1349
				array(
1350
					array(
1351
						'User' => array(
1352
							'id' => '1',
1353
							'dummy' => '1',
1354
						),
1355
					),
1356
					array(
1357
						'User' => array(
1358
							'id' => '1',
1359
							'dummy' => '2',
1360
						),
1361
					),
1362
				),
1363
				// $expectedResults
1364
				array(
1365
					array(
1366
						'Article' => array(
1367
							'id' => '1',
1368
							'user_id' => '1',
1369
							'User' => array(
1370
								'id' => '1',
1371
								'dummy' => '1',
1372
							),
1373
						),
1374
					),
1375
					array(
1376
						'Article' => array(
1377
							'id' => '3',
1378
							'user_id' => '1',
1379
							'User' => array(
1380
								'id' => '1',
1381
								'dummy' => '2',
1382
							),
1383
						),
1384
					),
1385
				),
1386
				// }}}
1387
			),
1388
			array(
1389
				// {{{ #6 hasOne
1390
				'Comment',
1391
				'Attachment',
1392
				// $meta
1393
				array(
1394
					'parentAlias' => 'Comment',
1395
					'parentKey' => 'id',
1396
					'targetKey' => 'comment_id',
1397
					'aliasPath' => 'Comment.Attachment',
1398
					'propertyPath' => 'Comment.Attachment',
1399
					'options' => array(
1400
						'fields' => 'id',
1401
					),
1402
					'has' => true,
1403
					'belong' => false,
1404
					'many' => false,
1405
					'external' => false,
1406
				),
1407
				// $results
1408
				array(
1409
					array(
1410
						'Comment' => array(
1411
							'id' => '1',
1412
						),
1413
						'Attachment' => array(
1414
							'id' => null,
1415
							'comment_id' => null,
1416
						),
1417
					),
1418
					array(
1419
						'Comment' => array(
1420
							'id' => '5',
1421
						),
1422
						'Attachment' => array(
1423
							'id' => '1',
1424
							'comment_id' => '5',
1425
						),
1426
					)
1427
				),
1428
				// $fixtures
1429
				array('Comment', 'Attachment'),
1430
				// $expectedArgument
1431
				array(
1432
					array(
1433
						'Attachment' => array(),
1434
					),
1435
					array(
1436
						'Attachment' => array(
1437
							'id' => '1',
1438
							'comment_id' => '5',
1439
						),
1440
					),
1441
				),
1442
				// $expectedResults
1443
				array(
1444
					array(
1445
						'Comment' => array(
1446
							'id' => '1',
1447
							'Attachment' => array(),
1448
						),
1449
					),
1450
					array(
1451
						'Comment' => array(
1452
							'id' => '5',
1453
							'Attachment' => array(
1454
								'id' => '1',
1455
								'comment_id' => '5',
1456
							),
1457
						),
1458
					),
1459
				),
1460
				// }}}
1461
			),
1462
		);
1463
	}
1464
1465
/**
1466
 * Tests that no memory leak occurs
1467
 *
1468
 * @return void
1469
 */
1470
	public function testGarbageCollection() {
1471
		$this->loadFixtures('User', 'Article');
1472
		$User = ClassRegistry::init('User');
1473
1474
		for ($i = 0; $i < 1100; ++$i) {
1475
			EagerLoader::handleBeforeFind($User, array('contain' => 'Article'));
1476
		}
1477
1478
		$prop = new ReflectionProperty($this->EagerLoader, 'handlers');
1479
		$prop->setAccessible(true);
1480
		$handlers = $prop->getValue(null);
1481
		$this->assertEquals(1000, count($handlers));
1482
	}
1483
1484
/**
1485
 * Tests that an exception occurs if invalid ID specified
1486
 *
1487
 * @return void
1488
 *
1489
 * @expectedException UnexpectedValueException
1490
 * @expectedExceptionMessage EagerLoader "foo" is not found
1491
 */
1492
	public function testNotFound() {
1493
		$User = ClassRegistry::init('User');
1494
		EagerLoader::handleAfterFind($User, array(array('EagerLoaderModel' => array('id' => 'foo'))));
1495
	}
1496
}
1497