1
|
<?php
|
2
|
|
3
|
/**
|
4
|
* @file
|
5
|
* Tests for the Batch API.
|
6
|
*/
|
7
|
|
8
|
/**
|
9
|
* Tests for the Batch API.
|
10
|
*/
|
11
|
class BatchProcessingTestCase extends DrupalWebTestCase {
|
12
|
public static function getInfo() {
|
13
|
return array(
|
14
|
'name' => 'Batch processing',
|
15
|
'description' => 'Test batch processing in form and non-form workflow.',
|
16
|
'group' => 'Batch API',
|
17
|
);
|
18
|
}
|
19
|
|
20
|
function setUp() {
|
21
|
parent::setUp('batch_test');
|
22
|
}
|
23
|
|
24
|
/**
|
25
|
* Test batches triggered outside of form submission.
|
26
|
*/
|
27
|
function testBatchNoForm() {
|
28
|
// Displaying the page triggers batch 1.
|
29
|
$this->drupalGet('batch-test/no-form');
|
30
|
$this->assertBatchMessages($this->_resultMessages(1), t('Batch for step 2 performed successfully.'));
|
31
|
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
|
32
|
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
|
33
|
}
|
34
|
|
35
|
/**
|
36
|
* Test batches defined in a form submit handler.
|
37
|
*/
|
38
|
function testBatchForm() {
|
39
|
// Batch 0: no operation.
|
40
|
$edit = array('batch' => 'batch_0');
|
41
|
$this->drupalPost('batch-test/simple', $edit, 'Submit');
|
42
|
$this->assertBatchMessages($this->_resultMessages('batch_0'), t('Batch with no operation performed successfully.'));
|
43
|
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
|
44
|
|
45
|
// Batch 1: several simple operations.
|
46
|
$edit = array('batch' => 'batch_1');
|
47
|
$this->drupalPost('batch-test/simple', $edit, 'Submit');
|
48
|
$this->assertBatchMessages($this->_resultMessages('batch_1'), t('Batch with simple operations performed successfully.'));
|
49
|
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
|
50
|
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
|
51
|
|
52
|
// Batch 2: one multistep operation.
|
53
|
$edit = array('batch' => 'batch_2');
|
54
|
$this->drupalPost('batch-test/simple', $edit, 'Submit');
|
55
|
$this->assertBatchMessages($this->_resultMessages('batch_2'), t('Batch with multistep operation performed successfully.'));
|
56
|
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), t('Execution order was correct.'));
|
57
|
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
|
58
|
|
59
|
// Batch 3: simple + multistep combined.
|
60
|
$edit = array('batch' => 'batch_3');
|
61
|
$this->drupalPost('batch-test/simple', $edit, 'Submit');
|
62
|
$this->assertBatchMessages($this->_resultMessages('batch_3'), t('Batch with simple and multistep operations performed successfully.'));
|
63
|
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_3'), t('Execution order was correct.'));
|
64
|
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
|
65
|
|
66
|
// Batch 4: nested batch.
|
67
|
$edit = array('batch' => 'batch_4');
|
68
|
$this->drupalPost('batch-test/simple', $edit, 'Submit');
|
69
|
$this->assertBatchMessages($this->_resultMessages('batch_4'), t('Nested batch performed successfully.'));
|
70
|
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_4'), t('Execution order was correct.'));
|
71
|
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
|
72
|
}
|
73
|
|
74
|
/**
|
75
|
* Test batches defined in a multistep form.
|
76
|
*/
|
77
|
function testBatchFormMultistep() {
|
78
|
$this->drupalGet('batch-test/multistep');
|
79
|
$this->assertText('step 1', t('Form is displayed in step 1.'));
|
80
|
|
81
|
// First step triggers batch 1.
|
82
|
$this->drupalPost(NULL, array(), 'Submit');
|
83
|
$this->assertBatchMessages($this->_resultMessages('batch_1'), t('Batch for step 1 performed successfully.'));
|
84
|
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_1'), t('Execution order was correct.'));
|
85
|
$this->assertText('step 2', t('Form is displayed in step 2.'));
|
86
|
|
87
|
// Second step triggers batch 2.
|
88
|
$this->drupalPost(NULL, array(), 'Submit');
|
89
|
$this->assertBatchMessages($this->_resultMessages('batch_2'), t('Batch for step 2 performed successfully.'));
|
90
|
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_2'), t('Execution order was correct.'));
|
91
|
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
|
92
|
}
|
93
|
|
94
|
/**
|
95
|
* Test batches defined in different submit handlers on the same form.
|
96
|
*/
|
97
|
function testBatchFormMultipleBatches() {
|
98
|
// Batches 1, 2 and 3 are triggered in sequence by different submit
|
99
|
// handlers. Each submit handler modify the submitted 'value'.
|
100
|
$value = rand(0, 255);
|
101
|
$edit = array('value' => $value);
|
102
|
$this->drupalPost('batch-test/chained', $edit, 'Submit');
|
103
|
// Check that result messages are present and in the correct order.
|
104
|
$this->assertBatchMessages($this->_resultMessages('chained'), t('Batches defined in separate submit handlers performed successfully.'));
|
105
|
// The stack contains execution order of batch callbacks and submit
|
106
|
// hanlders and logging of corresponding $form_state[{values'].
|
107
|
$this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), t('Execution order was correct, and $form_state is correctly persisted.'));
|
108
|
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
|
109
|
}
|
110
|
|
111
|
/**
|
112
|
* Test batches defined in a programmatically submitted form.
|
113
|
*
|
114
|
* Same as above, but the form is submitted through drupal_form_execute().
|
115
|
*/
|
116
|
function testBatchFormProgrammatic() {
|
117
|
// Batches 1, 2 and 3 are triggered in sequence by different submit
|
118
|
// handlers. Each submit handler modify the submitted 'value'.
|
119
|
$value = rand(0, 255);
|
120
|
$this->drupalGet('batch-test/programmatic/' . $value);
|
121
|
// Check that result messages are present and in the correct order.
|
122
|
$this->assertBatchMessages($this->_resultMessages('chained'), t('Batches defined in separate submit handlers performed successfully.'));
|
123
|
// The stack contains execution order of batch callbacks and submit
|
124
|
// hanlders and logging of corresponding $form_state[{values'].
|
125
|
$this->assertEqual(batch_test_stack(), $this->_resultStack('chained', $value), t('Execution order was correct, and $form_state is correctly persisted.'));
|
126
|
$this->assertText('Got out of a programmatic batched form.', t('Page execution continues normally.'));
|
127
|
}
|
128
|
|
129
|
/**
|
130
|
* Test that drupal_form_submit() can run within a batch operation.
|
131
|
*/
|
132
|
function testDrupalFormSubmitInBatch() {
|
133
|
// Displaying the page triggers a batch that programmatically submits a
|
134
|
// form.
|
135
|
$value = rand(0, 255);
|
136
|
$this->drupalGet('batch-test/nested-programmatic/' . $value);
|
137
|
$this->assertEqual(batch_test_stack(), array('mock form submitted with value = ' . $value), t('drupal_form_submit() ran successfully within a batch operation.'));
|
138
|
}
|
139
|
|
140
|
/**
|
141
|
* Test batches that return $context['finished'] > 1 do in fact complete.
|
142
|
* See http://drupal.org/node/600836
|
143
|
*/
|
144
|
function testBatchLargePercentage() {
|
145
|
// Displaying the page triggers batch 5.
|
146
|
$this->drupalGet('batch-test/large-percentage');
|
147
|
$this->assertBatchMessages($this->_resultMessages(1), t('Batch for step 2 performed successfully.'));
|
148
|
$this->assertEqual(batch_test_stack(), $this->_resultStack('batch_5'), t('Execution order was correct.'));
|
149
|
$this->assertText('Redirection successful.', t('Redirection after batch execution is correct.'));
|
150
|
}
|
151
|
|
152
|
|
153
|
/**
|
154
|
* Will trigger a pass if the texts were found in order in the raw content.
|
155
|
*
|
156
|
* @param $texts
|
157
|
* Array of raw strings to look for .
|
158
|
* @param $message
|
159
|
* Message to display.
|
160
|
* @return
|
161
|
* TRUE on pass, FALSE on fail.
|
162
|
*/
|
163
|
function assertBatchMessages($texts, $message) {
|
164
|
$pattern = '|' . implode('.*', $texts) .'|s';
|
165
|
return $this->assertPattern($pattern, $message);
|
166
|
}
|
167
|
|
168
|
/**
|
169
|
* Helper function: return expected execution stacks for the test batches.
|
170
|
*/
|
171
|
function _resultStack($id, $value = 0) {
|
172
|
$stack = array();
|
173
|
switch ($id) {
|
174
|
case 'batch_1':
|
175
|
for ($i = 1; $i <= 10; $i++) {
|
176
|
$stack[] = "op 1 id $i";
|
177
|
}
|
178
|
break;
|
179
|
|
180
|
case 'batch_2':
|
181
|
for ($i = 1; $i <= 10; $i++) {
|
182
|
$stack[] = "op 2 id $i";
|
183
|
}
|
184
|
break;
|
185
|
|
186
|
case 'batch_3':
|
187
|
for ($i = 1; $i <= 5; $i++) {
|
188
|
$stack[] = "op 1 id $i";
|
189
|
}
|
190
|
for ($i = 1; $i <= 5; $i++) {
|
191
|
$stack[] = "op 2 id $i";
|
192
|
}
|
193
|
for ($i = 6; $i <= 10; $i++) {
|
194
|
$stack[] = "op 1 id $i";
|
195
|
}
|
196
|
for ($i = 6; $i <= 10; $i++) {
|
197
|
$stack[] = "op 2 id $i";
|
198
|
}
|
199
|
break;
|
200
|
|
201
|
case 'batch_4':
|
202
|
for ($i = 1; $i <= 5; $i++) {
|
203
|
$stack[] = "op 1 id $i";
|
204
|
}
|
205
|
$stack[] = 'setting up batch 2';
|
206
|
for ($i = 6; $i <= 10; $i++) {
|
207
|
$stack[] = "op 1 id $i";
|
208
|
}
|
209
|
$stack = array_merge($stack, $this->_resultStack('batch_2'));
|
210
|
break;
|
211
|
|
212
|
case 'batch_5':
|
213
|
for ($i = 1; $i <= 10; $i++) {
|
214
|
$stack[] = "op 5 id $i";
|
215
|
}
|
216
|
break;
|
217
|
|
218
|
case 'chained':
|
219
|
$stack[] = 'submit handler 1';
|
220
|
$stack[] = 'value = ' . $value;
|
221
|
$stack = array_merge($stack, $this->_resultStack('batch_1'));
|
222
|
$stack[] = 'submit handler 2';
|
223
|
$stack[] = 'value = ' . ($value + 1);
|
224
|
$stack = array_merge($stack, $this->_resultStack('batch_2'));
|
225
|
$stack[] = 'submit handler 3';
|
226
|
$stack[] = 'value = ' . ($value + 2);
|
227
|
$stack[] = 'submit handler 4';
|
228
|
$stack[] = 'value = ' . ($value + 3);
|
229
|
$stack = array_merge($stack, $this->_resultStack('batch_3'));
|
230
|
break;
|
231
|
}
|
232
|
return $stack;
|
233
|
}
|
234
|
|
235
|
/**
|
236
|
* Helper function: return expected result messages for the test batches.
|
237
|
*/
|
238
|
function _resultMessages($id) {
|
239
|
$messages = array();
|
240
|
|
241
|
switch ($id) {
|
242
|
case 'batch_0':
|
243
|
$messages[] = 'results for batch 0<br />none';
|
244
|
break;
|
245
|
|
246
|
case 'batch_1':
|
247
|
$messages[] = 'results for batch 1<br />op 1: processed 10 elements';
|
248
|
break;
|
249
|
|
250
|
case 'batch_2':
|
251
|
$messages[] = 'results for batch 2<br />op 2: processed 10 elements';
|
252
|
break;
|
253
|
|
254
|
case 'batch_3':
|
255
|
$messages[] = 'results for batch 3<br />op 1: processed 10 elements<br />op 2: processed 10 elements';
|
256
|
break;
|
257
|
|
258
|
case 'batch_4':
|
259
|
$messages[] = 'results for batch 4<br />op 1: processed 10 elements';
|
260
|
$messages = array_merge($messages, $this->_resultMessages('batch_2'));
|
261
|
break;
|
262
|
|
263
|
case 'batch_5':
|
264
|
$messages[] = 'results for batch 5<br />op 1: processed 10 elements. $context[\'finished\'] > 1 returned from batch process, with success.';
|
265
|
break;
|
266
|
|
267
|
case 'chained':
|
268
|
$messages = array_merge($messages, $this->_resultMessages('batch_1'));
|
269
|
$messages = array_merge($messages, $this->_resultMessages('batch_2'));
|
270
|
$messages = array_merge($messages, $this->_resultMessages('batch_3'));
|
271
|
break;
|
272
|
}
|
273
|
return $messages;
|
274
|
}
|
275
|
}
|
276
|
|
277
|
/**
|
278
|
* Tests for the Batch API Progress page.
|
279
|
*/
|
280
|
class BatchPageTestCase extends DrupalWebTestCase {
|
281
|
public static function getInfo() {
|
282
|
return array(
|
283
|
'name' => 'Batch progress page',
|
284
|
'description' => 'Test the content of the progress page.',
|
285
|
'group' => 'Batch API',
|
286
|
);
|
287
|
}
|
288
|
|
289
|
function setUp() {
|
290
|
parent::setUp('batch_test');
|
291
|
}
|
292
|
|
293
|
/**
|
294
|
* Tests that the batch API progress page uses the correct theme.
|
295
|
*/
|
296
|
function testBatchProgressPageTheme() {
|
297
|
// Make sure that the page which starts the batch (an administrative page)
|
298
|
// is using a different theme than would normally be used by the batch API.
|
299
|
variable_set('theme_default', 'bartik');
|
300
|
variable_set('admin_theme', 'seven');
|
301
|
// Log in as an administrator who can see the administrative theme.
|
302
|
$admin_user = $this->drupalCreateUser(array('view the administration theme'));
|
303
|
$this->drupalLogin($admin_user);
|
304
|
// Visit an administrative page that runs a test batch, and check that the
|
305
|
// theme that was used during batch execution (which the batch callback
|
306
|
// function saved as a variable) matches the theme used on the
|
307
|
// administrative page.
|
308
|
$this->drupalGet('admin/batch-test/test-theme');
|
309
|
// The stack should contain the name of the theme used on the progress
|
310
|
// page.
|
311
|
$this->assertEqual(batch_test_stack(), array('seven'), t('A progressive batch correctly uses the theme of the page that started the batch.'));
|
312
|
}
|
313
|
}
|
314
|
|
315
|
/**
|
316
|
* Tests the function _batch_api_percentage() to make sure that the rounding
|
317
|
* works properly in all cases.
|
318
|
*/
|
319
|
class BatchPercentagesUnitTestCase extends DrupalUnitTestCase {
|
320
|
protected $testCases = array();
|
321
|
|
322
|
public static function getInfo() {
|
323
|
return array(
|
324
|
'name' => 'Batch percentages',
|
325
|
'description' => 'Unit tests of progress percentage rounding.',
|
326
|
'group' => 'Batch API',
|
327
|
);
|
328
|
}
|
329
|
|
330
|
function setUp() {
|
331
|
// Set up an array of test cases, where the expected values are the keys,
|
332
|
// and the values are arrays with the keys 'total' and 'current',
|
333
|
// corresponding with the function parameters of _batch_api_percentage().
|
334
|
$this->testCases = array(
|
335
|
// 1/2 is 50%.
|
336
|
'50' => array('total' => 2, 'current' => 1),
|
337
|
// Though we should never encounter a case where the current set is set
|
338
|
// 0, if we did, we should get 0%.
|
339
|
'0' => array('total' => 3, 'current' => 0),
|
340
|
// 1/3 is closer to 33% than to 34%.
|
341
|
'33' => array('total' => 3, 'current' => 1),
|
342
|
// 2/3 is closer to 67% than to 66%.
|
343
|
'67' => array('total' => 3, 'current' => 2),
|
344
|
// 1/199 should round up to 1%.
|
345
|
'1' => array('total' => 199, 'current' => 1),
|
346
|
// 198/199 should round down to 99%.
|
347
|
'99' => array('total' => 199, 'current' => 198),
|
348
|
// 199/200 would have rounded up to 100%, which would give the false
|
349
|
// impression of being finished, so we add another digit and should get
|
350
|
// 99.5%.
|
351
|
'99.5' => array('total' => 200, 'current' => 199),
|
352
|
// The same logic holds for 1/200: we should get 0.5%.
|
353
|
'0.5' => array('total' => 200, 'current' => 1),
|
354
|
// Numbers that come out evenly, such as 50/200, should be forced to have
|
355
|
// extra digits for consistancy.
|
356
|
'25.0' => array('total' => 200, 'current' => 50),
|
357
|
// Regardless of number of digits we're using, 100% should always just be
|
358
|
// 100%.
|
359
|
'100' => array('total' => 200, 'current' => 200),
|
360
|
// 1998/1999 should similarly round down to 99.9%.
|
361
|
'99.9' => array('total' => 1999, 'current' => 1998),
|
362
|
// 1999/2000 should add another digit and go to 99.95%.
|
363
|
'99.95' => array('total' => 2000, 'current' => 1999),
|
364
|
// 19999/20000 should add yet another digit and go to 99.995%.
|
365
|
'99.995' => array('total' => 20000, 'current' => 19999),
|
366
|
// The next five test cases simulate a batch with a single operation
|
367
|
// ('total' equals 1) that takes several steps to complete. Within the
|
368
|
// operation, we imagine that there are 501 items to process, and 100 are
|
369
|
// completed during each step. The percentages we get back should be
|
370
|
// rounded the usual way for the first few passes (i.e., 20%, 40%, etc.),
|
371
|
// but for the last pass through, when 500 out of 501 items have been
|
372
|
// processed, we do not want to round up to 100%, since that would
|
373
|
// erroneously indicate that the processing is complete.
|
374
|
'20' => array('total' => 1, 'current' => 100/501),
|
375
|
'40' => array('total' => 1, 'current' => 200/501),
|
376
|
'60' => array('total' => 1, 'current' => 300/501),
|
377
|
'80' => array('total' => 1, 'current' => 400/501),
|
378
|
'99.8' => array('total' => 1, 'current' => 500/501),
|
379
|
);
|
380
|
require_once DRUPAL_ROOT . '/includes/batch.inc';
|
381
|
parent::setUp();
|
382
|
}
|
383
|
|
384
|
/**
|
385
|
* Test the _batch_api_percentage() function.
|
386
|
*/
|
387
|
function testBatchPercentages() {
|
388
|
foreach ($this->testCases as $expected_result => $arguments) {
|
389
|
// PHP sometimes casts numeric strings that are array keys to integers,
|
390
|
// cast them back here.
|
391
|
$expected_result = (string) $expected_result;
|
392
|
$total = $arguments['total'];
|
393
|
$current = $arguments['current'];
|
394
|
$actual_result = _batch_api_percentage($total, $current);
|
395
|
if ($actual_result === $expected_result) {
|
396
|
$this->pass(t('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, and got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result)));
|
397
|
}
|
398
|
else {
|
399
|
$this->fail(t('Expected the batch api percentage at the state @numerator/@denominator to be @expected%, but got @actual%.', array('@numerator' => $current, '@denominator' => $total, '@expected' => $expected_result, '@actual' => $actual_result)));
|
400
|
}
|
401
|
}
|
402
|
}
|
403
|
}
|