cms/drupal/modules/simpletest/tests/batch.test
changeset 541 e756a8c72c3d
equal deleted inserted replaced
540:07239de796bb 541:e756a8c72c3d
       
     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 }