1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Diff\Tests\Patcher; |
4
|
|
|
|
5
|
|
|
use Diff\Comparer\CallbackComparer; |
6
|
|
|
use Diff\DiffOp\Diff\Diff; |
7
|
|
|
use Diff\DiffOp\DiffOpAdd; |
8
|
|
|
use Diff\DiffOp\DiffOpChange; |
9
|
|
|
use Diff\DiffOp\DiffOpRemove; |
10
|
|
|
use Diff\Patcher\MapPatcher; |
11
|
|
|
use Diff\Patcher\Patcher; |
12
|
|
|
use Diff\Tests\DiffTestCase; |
13
|
|
|
|
14
|
|
|
/** |
15
|
|
|
* @covers Diff\Patcher\MapPatcher |
16
|
|
|
* @covers Diff\Patcher\ThrowingPatcher |
17
|
|
|
* |
18
|
|
|
* @group Diff |
19
|
|
|
* @group DiffPatcher |
20
|
|
|
* @group MapPatcherTest |
21
|
|
|
* |
22
|
|
|
* @licence GNU GPL v2+ |
23
|
|
|
* @author Jeroen De Dauw < [email protected] > |
24
|
|
|
* @author Daniel Kinzler |
25
|
|
|
*/ |
26
|
|
|
class MapPatcherTest extends DiffTestCase { |
27
|
|
|
|
28
|
|
|
public function patchProvider() { |
29
|
|
|
$argLists = array(); |
30
|
|
|
|
31
|
|
|
$patcher = new MapPatcher(); |
32
|
|
|
$base = array(); |
33
|
|
|
$diff = new Diff(); |
34
|
|
|
$expected = array(); |
35
|
|
|
|
36
|
|
|
$argLists['all empty'] = array( $patcher, $base, $diff, $expected ); |
37
|
|
|
|
38
|
|
|
|
39
|
|
|
$patcher = new MapPatcher(); |
40
|
|
|
$base = array( 'foo', 'bar' => array( 'baz' ) ); |
41
|
|
|
$diff = new Diff(); |
42
|
|
|
$expected = array( 'foo', 'bar' => array( 'baz' ) ); |
43
|
|
|
|
44
|
|
|
$argLists['empty patch'] = array( $patcher, $base, $diff, $expected ); |
45
|
|
|
|
46
|
|
|
|
47
|
|
|
$patcher = new MapPatcher(); |
48
|
|
|
$base = array( 'foo', 'bar' => array( 'baz' ) ); |
49
|
|
|
$diff = new Diff( array( 'bah' => new DiffOpAdd( 'blah' ) ) ); |
50
|
|
|
$expected = array( 'foo', 'bar' => array( 'baz' ), 'bah' => 'blah' ); |
51
|
|
|
|
52
|
|
|
$argLists['add'] = array( $patcher, $base, $diff, $expected ); |
53
|
|
|
|
54
|
|
|
|
55
|
|
|
$patcher = new MapPatcher(); |
56
|
|
|
$base = array( 'foo', 'bar' => array( 'baz' ) ); |
57
|
|
|
$diff = new Diff( array( 'bah' => new DiffOpAdd( 'blah' ) ) ); |
58
|
|
|
$expected = array( 'foo', 'bar' => array( 'baz' ), 'bah' => 'blah' ); |
59
|
|
|
|
60
|
|
|
$argLists['add2'] = array( $patcher, $base, $diff, $expected ); //FIXME: dupe? |
61
|
|
|
|
62
|
|
|
|
63
|
|
|
$patcher = new MapPatcher(); |
64
|
|
|
$base = array(); |
65
|
|
|
$diff = new Diff( array( |
66
|
|
|
'foo' => new DiffOpAdd( 'bar' ), |
67
|
|
|
'bah' => new DiffOpAdd( 'blah' ) |
68
|
|
|
) ); |
69
|
|
|
$expected = array( |
70
|
|
|
'foo' => 'bar', |
71
|
|
|
'bah' => 'blah' |
72
|
|
|
); |
73
|
|
|
|
74
|
|
|
$argLists['add to empty base'] = array( $patcher, $base, $diff, $expected ); |
75
|
|
|
|
76
|
|
|
|
77
|
|
|
$patcher = new MapPatcher(); |
78
|
|
|
$base = array( |
79
|
|
|
'enwiki' => array( |
80
|
|
|
'name' => 'Nyan Cat', |
81
|
|
|
'badges' => array( 'FA' ) |
82
|
|
|
) |
83
|
|
|
); |
84
|
|
|
$diff = new Diff( array( |
85
|
|
|
'nlwiki' => new Diff( array( |
86
|
|
|
'name' => new DiffOpAdd( 'Nyan Cat' ), |
87
|
|
|
'badges' => new Diff( array( |
88
|
|
|
new DiffOpAdd( 'J approves' ), |
89
|
|
|
), false ), |
90
|
|
|
), true ), |
91
|
|
|
), true ); |
92
|
|
|
$expected = array( |
93
|
|
|
'enwiki' => array( |
94
|
|
|
'name' => 'Nyan Cat', |
95
|
|
|
'badges' => array( 'FA' ) |
96
|
|
|
), |
97
|
|
|
|
98
|
|
|
'nlwiki' => array( |
99
|
|
|
'name' => 'Nyan Cat', |
100
|
|
|
'badges' => array( 'J approves' ) |
101
|
|
|
) |
102
|
|
|
); |
103
|
|
|
|
104
|
|
|
$argLists['add to non-existent key'] = array( $patcher, $base, $diff, $expected ); |
105
|
|
|
|
106
|
|
|
|
107
|
|
|
$patcher = new MapPatcher(); |
108
|
|
|
$base = array( |
109
|
|
|
'foo' => 'bar', |
110
|
|
|
'nyan' => 'cat', |
111
|
|
|
'bah' => 'blah', |
112
|
|
|
); |
113
|
|
|
$diff = new Diff( array( |
114
|
|
|
'foo' => new DiffOpRemove( 'bar' ), |
115
|
|
|
'bah' => new DiffOpRemove( 'blah' ), |
116
|
|
|
) ); |
117
|
|
|
$expected = array( |
118
|
|
|
'nyan' => 'cat' |
119
|
|
|
); |
120
|
|
|
|
121
|
|
|
$argLists['remove'] = array( $patcher, $base, $diff, $expected ); |
122
|
|
|
|
123
|
|
|
|
124
|
|
|
$patcher = new MapPatcher(); |
125
|
|
|
$base = array( |
126
|
|
|
'foo' => 'bar', |
127
|
|
|
'nyan' => 'cat', |
128
|
|
|
'spam' => 'blah', |
129
|
|
|
'bah' => 'blah', |
130
|
|
|
); |
131
|
|
|
$diff = new Diff( array( |
132
|
|
|
'foo' => new DiffOpChange( 'bar', 'baz' ), |
133
|
|
|
'bah' => new DiffOpRemove( 'blah' ), |
134
|
|
|
'oh' => new DiffOpAdd( 'noez' ), |
135
|
|
|
) ); |
136
|
|
|
$expected = array( |
137
|
|
|
'foo' => 'baz', |
138
|
|
|
'nyan' => 'cat', |
139
|
|
|
'spam' => 'blah', |
140
|
|
|
'oh' => 'noez', |
141
|
|
|
); |
142
|
|
|
|
143
|
|
|
$argLists['change/add/remove'] = array( $patcher, $base, $diff, $expected ); |
144
|
|
|
|
145
|
|
|
|
146
|
|
|
$patcher = new MapPatcher(); |
147
|
|
|
$base = array( |
148
|
|
|
'foo' => 'bar', |
149
|
|
|
); |
150
|
|
|
$diff = new Diff( array( |
151
|
|
|
'baz' => new Diff( array( new DiffOpAdd( 'ny' ), new DiffOpAdd( 'an' ) ), false ), |
152
|
|
|
) ); |
153
|
|
|
$expected = array( |
154
|
|
|
'foo' => 'bar', |
155
|
|
|
'baz' => array( 'ny', 'an' ), |
156
|
|
|
); |
157
|
|
|
|
158
|
|
|
$argLists['add to substructure'] = array( $patcher, $base, $diff, $expected ); |
159
|
|
|
|
160
|
|
|
|
161
|
|
|
// ---- conflicts ---- |
162
|
|
|
|
163
|
|
|
$patcher = new MapPatcher(); |
164
|
|
|
$base = array(); |
165
|
|
|
$diff = new Diff( array( |
166
|
|
|
'baz' => new DiffOpRemove( 'X' ), |
167
|
|
|
) ); |
168
|
|
|
$expected = $base; |
169
|
|
|
|
170
|
|
|
$argLists['conflict: remove missing'] = array( $patcher, $base, $diff, $expected ); |
171
|
|
|
|
172
|
|
|
|
173
|
|
|
$patcher = new MapPatcher(); |
174
|
|
|
$base = array( 'baz' => 'Y' ); |
175
|
|
|
$diff = new Diff( array( |
176
|
|
|
'baz' => new DiffOpRemove( 'X' ), |
177
|
|
|
) ); |
178
|
|
|
$expected = $base; |
179
|
|
|
|
180
|
|
|
$argLists['conflict: remove mismatching value'] = array( $patcher, $base, $diff, $expected ); |
181
|
|
|
|
182
|
|
|
|
183
|
|
|
$patcher = new MapPatcher(); |
184
|
|
|
$base = array( 'baz' => 'Y' ); |
185
|
|
|
$diff = new Diff( array( |
186
|
|
|
'baz' => new DiffOpAdd( 'X' ), |
187
|
|
|
) ); |
188
|
|
|
$expected = $base; |
189
|
|
|
|
190
|
|
|
$argLists['conflict: add existing'] = array( $patcher, $base, $diff, $expected ); |
191
|
|
|
|
192
|
|
|
|
193
|
|
|
$patcher = new MapPatcher(); |
194
|
|
|
$base = array( 'baz' => 'Y' ); |
195
|
|
|
$diff = new Diff( array( |
196
|
|
|
'baz' => new DiffOpChange( 'X', 'Z' ), |
197
|
|
|
) ); |
198
|
|
|
$expected = $base; |
199
|
|
|
|
200
|
|
|
$argLists['conflict: change mismatching value'] = array( $patcher, $base, $diff, $expected ); |
201
|
|
|
|
202
|
|
|
|
203
|
|
|
$patcher = new MapPatcher(); |
204
|
|
|
$base = array( |
205
|
|
|
'foo' => 'bar', |
206
|
|
|
'nyan' => 'cat', |
207
|
|
|
'spam' => 'blah', |
208
|
|
|
'bah' => 'blah', |
209
|
|
|
); |
210
|
|
|
$diff = new Diff( array( |
211
|
|
|
'foo' => new DiffOpChange( 'bar', 'var' ), |
212
|
|
|
'nyan' => new DiffOpRemove( 'fat' ), |
213
|
|
|
'bah' => new DiffOpChange( 'blubb', 'clubb' ), |
214
|
|
|
'yea' => new DiffOpAdd( 'stuff' ), |
215
|
|
|
) ); |
216
|
|
|
$expected = array( |
217
|
|
|
'foo' => 'var', |
218
|
|
|
'nyan' => 'cat', |
219
|
|
|
'spam' => 'blah', |
220
|
|
|
'bah' => 'blah', |
221
|
|
|
'yea' => 'stuff', |
222
|
|
|
); |
223
|
|
|
|
224
|
|
|
$argLists['some mixed conflicts'] = array( $patcher, $base, $diff, $expected ); |
225
|
|
|
|
226
|
|
|
return $argLists; |
227
|
|
|
} |
228
|
|
|
|
229
|
|
|
/** |
230
|
|
|
* @dataProvider patchProvider |
231
|
|
|
* |
232
|
|
|
* @param Patcher $patcher |
233
|
|
|
* @param array $base |
234
|
|
|
* @param Diff $diff |
235
|
|
|
* @param array $expected |
236
|
|
|
*/ |
237
|
|
|
public function testPatch( Patcher $patcher, array $base, Diff $diff, array $expected ) { |
238
|
|
|
$actual = $patcher->patch( $base, $diff ); |
239
|
|
|
|
240
|
|
|
$this->assertArrayEquals( $expected, $actual, true, true ); |
241
|
|
|
} |
242
|
|
|
|
243
|
|
|
public function getApplicableDiffProvider() { |
244
|
|
|
// Diff, current object, expected |
245
|
|
|
$argLists = array(); |
246
|
|
|
|
247
|
|
|
$diff = new Diff( array(), true ); |
248
|
|
|
$currentObject = array(); |
249
|
|
|
$expected = clone $diff; |
250
|
|
|
|
251
|
|
|
$argLists[] = array( $diff, $currentObject, $expected, 'Empty diff should remain empty on empty base' ); |
252
|
|
|
|
253
|
|
|
|
254
|
|
|
$diff = new Diff( array(), true ); |
255
|
|
|
|
256
|
|
|
$currentObject = array( 'foo' => 0, 'bar' => 1 ); |
257
|
|
|
|
258
|
|
|
$expected = clone $diff; |
259
|
|
|
|
260
|
|
|
$argLists[] = array( $diff, $currentObject, $expected, 'Empty diff should remain empty on non-empty base' ); |
261
|
|
|
|
262
|
|
|
|
263
|
|
|
$diff = new Diff( array( |
264
|
|
|
'foo' => new DiffOpChange( 0, 42 ), |
265
|
|
|
'bar' => new DiffOpChange( 1, 9001 ), |
266
|
|
|
), true ); |
267
|
|
|
|
268
|
|
|
$currentObject = array( 'foo' => 0, 'bar' => 1 ); |
269
|
|
|
|
270
|
|
|
$expected = clone $diff; |
271
|
|
|
|
272
|
|
|
$argLists[] = array( $diff, $currentObject, $expected, 'Diff should not be altered on matching base' ); |
273
|
|
|
|
274
|
|
|
|
275
|
|
|
$diff = new Diff( array( |
276
|
|
|
'foo' => new DiffOpChange( 0, 42 ), |
277
|
|
|
'bar' => new DiffOpChange( 1, 9001 ), |
278
|
|
|
), true ); |
279
|
|
|
$currentObject = array(); |
280
|
|
|
|
281
|
|
|
$expected = new Diff( array(), true ); |
282
|
|
|
|
283
|
|
|
$argLists[] = array( $diff, $currentObject, $expected, 'Diff with only change ops should be empty on empty base' ); |
284
|
|
|
|
285
|
|
|
|
286
|
|
|
$diff = new Diff( array( |
287
|
|
|
'foo' => new DiffOpChange( 0, 42 ), |
288
|
|
|
'bar' => new DiffOpChange( 1, 9001 ), |
289
|
|
|
), true ); |
290
|
|
|
|
291
|
|
|
$currentObject = array( 'foo' => 'something else', 'bar' => 1, 'baz' => 'o_O' ); |
292
|
|
|
|
293
|
|
|
$expected = new Diff( array( |
294
|
|
|
'bar' => new DiffOpChange( 1, 9001 ), |
295
|
|
|
), true ); |
296
|
|
|
|
297
|
|
|
$argLists[] = array( $diff, $currentObject, $expected, 'Only change ops present in the base should be retained' ); |
298
|
|
|
|
299
|
|
|
|
300
|
|
|
$diff = new Diff( array( |
301
|
|
|
'bar' => new DiffOpRemove( 9001 ), |
302
|
|
|
), true ); |
303
|
|
|
|
304
|
|
|
$currentObject = array(); |
305
|
|
|
|
306
|
|
|
$expected = new Diff( array(), true ); |
307
|
|
|
|
308
|
|
|
$argLists[] = array( $diff, $currentObject, $expected, 'Remove ops should be removed on empty base' ); |
309
|
|
|
|
310
|
|
|
|
311
|
|
|
$diff = new Diff( array( |
312
|
|
|
'foo' => new DiffOpAdd( 42 ), |
313
|
|
|
'bar' => new DiffOpRemove( 9001 ), |
314
|
|
|
), true ); |
315
|
|
|
|
316
|
|
|
$currentObject = array( 'foo' => 'bar' ); |
317
|
|
|
|
318
|
|
|
$expected = new Diff( array(), true ); |
319
|
|
|
|
320
|
|
|
$argLists[] = array( |
321
|
|
|
$diff, |
322
|
|
|
$currentObject, |
323
|
|
|
$expected, |
324
|
|
|
'Mismatching add ops and remove ops not present in base should be removed' |
325
|
|
|
); |
326
|
|
|
|
327
|
|
|
|
328
|
|
|
$diff = new Diff( array( |
329
|
|
|
'foo' => new DiffOpAdd( 42 ), |
330
|
|
|
'bar' => new DiffOpRemove( 9001 ), |
331
|
|
|
), true ); |
332
|
|
|
|
333
|
|
|
$currentObject = array( 'foo' => 42, 'bar' => 9001 ); |
334
|
|
|
|
335
|
|
|
$expected = new Diff( array( |
336
|
|
|
'bar' => new DiffOpRemove( 9001 ), |
337
|
|
|
), true ); |
338
|
|
|
|
339
|
|
|
$argLists[] = array( $diff, $currentObject, $expected, 'Remove ops present in base should be retained' ); |
340
|
|
|
|
341
|
|
|
|
342
|
|
|
$diff = new Diff( array( |
343
|
|
|
'foo' => new DiffOpAdd( 42 ), |
344
|
|
|
'bar' => new DiffOpRemove( 9001 ), |
345
|
|
|
), true ); |
346
|
|
|
|
347
|
|
|
$currentObject = array(); |
348
|
|
|
|
349
|
|
|
$expected = new Diff( array( |
350
|
|
|
'foo' => new DiffOpAdd( 42 ), |
351
|
|
|
), true ); |
352
|
|
|
|
353
|
|
|
$argLists[] = array( |
354
|
|
|
$diff, |
355
|
|
|
$currentObject, |
356
|
|
|
$expected, |
357
|
|
|
'Add ops not present in the base should be retained (MapDiff)' |
358
|
|
|
); |
359
|
|
|
|
360
|
|
|
|
361
|
|
|
$diff = new Diff( array( |
362
|
|
|
'foo' => new Diff( array( 'bar' => new DiffOpChange( 0, 1 ) ), true ), |
363
|
|
|
'le-non-existing-element' => new Diff( array( 'bar' => new DiffOpChange( 0, 1 ) ), true ), |
364
|
|
|
'spam' => new Diff( array( new DiffOpAdd( 42 ) ), false ), |
365
|
|
|
new DiffOpAdd( 9001 ), |
366
|
|
|
), true ); |
367
|
|
|
|
368
|
|
|
$currentObject = array( |
369
|
|
|
'foo' => array( 'bar' => 0, 'baz' => 'O_o' ), |
370
|
|
|
'spam' => array( 23, 'ohi' ) |
371
|
|
|
); |
372
|
|
|
|
373
|
|
|
$expected = new Diff( array( |
374
|
|
|
'foo' => new Diff( array( 'bar' => new DiffOpChange( 0, 1 ) ), true ), |
375
|
|
|
'spam' => new Diff( array( new DiffOpAdd( 42 ) ), false ), |
376
|
|
|
new DiffOpAdd( 9001 ), |
377
|
|
|
), true ); |
378
|
|
|
|
379
|
|
|
$argLists[] = array( $diff, $currentObject, $expected, 'Recursion should work properly' ); |
380
|
|
|
|
381
|
|
|
return $argLists; |
382
|
|
|
} |
383
|
|
|
|
384
|
|
|
/** |
385
|
|
|
* @dataProvider getApplicableDiffProvider |
386
|
|
|
* |
387
|
|
|
* @param Diff $diff |
388
|
|
|
* @param array $currentObject |
389
|
|
|
* @param Diff $expected |
390
|
|
|
* @param string|null $message |
391
|
|
|
*/ |
392
|
|
|
public function testGetApplicableDiff( Diff $diff, array $currentObject, Diff $expected, $message = null ) { |
393
|
|
|
$patcher = new MapPatcher(); |
394
|
|
|
$actual = $patcher->getApplicableDiff( $currentObject, $diff ); |
395
|
|
|
|
396
|
|
|
$this->assertEquals( $expected->getOperations(), $actual->getOperations(), $message ); |
397
|
|
|
} |
398
|
|
|
|
399
|
|
|
public function testSetValueComparerToAlwaysFalse() { |
400
|
|
|
$patcher = new MapPatcher(); |
401
|
|
|
|
402
|
|
|
$patcher->setValueComparer( new CallbackComparer( function( $firstValue, $secondValue ) { |
|
|
|
|
403
|
|
|
return false; |
404
|
|
|
} ) ); |
405
|
|
|
|
406
|
|
|
$baseMap = array( |
407
|
|
|
'foo' => 42, |
408
|
|
|
'bar' => 9001, |
409
|
|
|
); |
410
|
|
|
|
411
|
|
|
$patch = new Diff( array( |
412
|
|
|
'foo' => new DiffOpChange( 42, 1337 ), |
413
|
|
|
'bar' => new DiffOpChange( 9001, 1337 ), |
414
|
|
|
) ); |
415
|
|
|
|
416
|
|
|
$patchedMap = $patcher->patch( $baseMap, $patch ); |
417
|
|
|
|
418
|
|
|
$this->assertEquals( $baseMap, $patchedMap ); |
419
|
|
|
} |
420
|
|
|
|
421
|
|
|
public function testSetValueComparerToAlwaysTrue() { |
422
|
|
|
$patcher = new MapPatcher(); |
423
|
|
|
|
424
|
|
|
$patcher->setValueComparer( new CallbackComparer( function( $firstValue, $secondValue ) { |
|
|
|
|
425
|
|
|
return true; |
426
|
|
|
} ) ); |
427
|
|
|
|
428
|
|
|
$baseMap = array( |
429
|
|
|
'foo' => 42, |
430
|
|
|
'bar' => 9001, |
431
|
|
|
); |
432
|
|
|
|
433
|
|
|
$patch = new Diff( array( |
434
|
|
|
'foo' => new DiffOpChange( 3, 1337 ), |
435
|
|
|
'bar' => new DiffOpChange( 3, 1337 ), |
436
|
|
|
) ); |
437
|
|
|
|
438
|
|
|
$expectedMap = array( |
439
|
|
|
'foo' => 1337, |
440
|
|
|
'bar' => 1337, |
441
|
|
|
); |
442
|
|
|
|
443
|
|
|
$patchedMap = $patcher->patch( $baseMap, $patch ); |
444
|
|
|
|
445
|
|
|
$this->assertEquals( $expectedMap, $patchedMap ); |
446
|
|
|
} |
447
|
|
|
|
448
|
|
|
public function testErrorOnUnknownDiffOpType() { |
449
|
|
|
$patcher = new MapPatcher(); |
450
|
|
|
|
451
|
|
|
$diffOp = $this->getMock( 'Diff\DiffOp\DiffOp' ); |
452
|
|
|
|
453
|
|
|
$diffOp->expects( $this->any() ) |
454
|
|
|
->method( 'getType' ) |
455
|
|
|
->will( $this->returnValue( 'diff' ) ); |
456
|
|
|
|
457
|
|
|
$diff = new Diff( array( $diffOp ), true ); |
458
|
|
|
|
459
|
|
|
$patcher->patch( array(), $diff ); |
460
|
|
|
|
461
|
|
|
$patcher->throwErrors(); |
462
|
|
|
$this->setExpectedException( 'Diff\Patcher\PatcherException' ); |
463
|
|
|
|
464
|
|
|
$patcher->patch( array(), $diff ); |
465
|
|
|
} |
466
|
|
|
|
467
|
|
|
} |
468
|
|
|
|
This check looks from parameters that have been defined for a function or method, but which are not used in the method body.