User login

Testing a patch and adding a simpletest

Testing a patch and adding a simpletest with Catch as my guide.

Steps taken, starting with checking out Drupal 7 dev (or head, until the code freeze).

(I got the link to the patch for the wget below from the issue, in this case http://drupal.org/node/305740 )

cd workspace/
cvs -z6 -d:pserver:anonymous:anonymous@cvs.drupal.org:/cvs/drupal co -d drupal-7 drupal
cd drupal-7/
wget http://drupal.org/files/issues/term_edit.patch
patch -p0 < term_edit.patch

(I screwed up on my first try above, see note at bottom.)

The above was a patch I was interested in, and in general I shouldn't apply two patches at once, though on the other hand we do want them to work together, so testing both is reasonable. The patch we are extending is also for Taxonomy module.

As we'll add the new test to this patch, we need to apply it:

wget http://drupal.org/files/issues/taxonomy_list_plus_test_0.patch
patch -p0 < taxonomy_list_plus_test_0.patch
patching file modules/node/node.admin.inc
patching file modules/node/node.module
patching file modules/taxonomy/taxonomy.admin.inc
patching file modules/taxonomy/taxonomy.test

To use the site (not just look at and modify the code blindly!), we have to create a database - phpmyadmin, command line, or a script of your own.

Then, as with the above, still from our drupal-7 (drupal root) directory:

cp sites/default/default.settings.php sites/default/settings.php
chmod a+w sites/default/settings.php
mkdir sites/default/files
chmod a+w sites/default/files
mkdir sites/default/private
chmod a+w sites/default/private

You should have put your Drupal checkout in, or symlink it from, your apache web root directory (or set up apache to look for it wherever you have it, and you can also take some other steps so you can access the test site through drupal7.dev or something, rather than localhost/drupal7, but we will use localhost/drupal-7 since Drupal should be tested to work in subdirectories on occasion anyway!)

so we go to http://localhost/drupal-7 and follow the instructions, mostly this means entering in the information for the database just created.

Congratulations, Drupal works!

We're replicating a form here, so model off of node module's test, in modules/node/node.test class PageEditTestCase:

<?php
class PageEditTestCase extends DrupalWebTestCase {
  function getInfo() {
    return array(
      'name'  => 'Page edit test',
      'description'  => t('We want a working edit for pages, uh?'),
      'group' => t('Node'));
  }
  function testPageEdit() {

    /* Prepare settings */
    variable_set('node_options_page', array('status', 'promote'));
    /* Prepare a user to do the stuff */
    $web_user = $this->drupalCreateUser(array('edit own page content', 'create page content'));
    $this->drupalLogin($web_user);
    $edit = array(
    'title' => '!SimpleTest! test title' . $this->randomName(20),
    'body' => '!SimpleTest! test body' . $this->randomName(200),
  );

    //Create the page to edit
    $this->drupalPost('node/add/page', $edit, t('Save'));

    $node = node_load(array('title' => $edit['title']));
    $this->assertNotNull($node, 'Node found in database');

    $this->clickLink(t('Edit'));
    $editurl = url("node/$node->nid/edit", array('absolute' => true));
    $acturl = $this->getURL();
    $this->assertEqual($editurl, $acturl);

    $this->assertText(t('Edit'), 'Edit text is here');
    $this->assertText(t($edit['title']), 'Hello, the random title');
    $this->assertText(t($edit['body']), 'test is over, the body\'s still there');

    $edit = array(
    'title' => '!SimpleTest! test title' . $this->randomName(20),
    'body' => '!SimpleTest! test body' . $this->randomName(200),
  );

    //edit the content of the page
    $this->drupalPost("node/$node->nid/edit", $edit, t('Save'));

    $this->assertText(t($edit['title']), 'Hello, the random title');
    $this->assertText(t($edit['body']), 'test is over, the body\'s still there');
  }

}
?>

I learned that you can't just make up the 'title' and 'body' stuff from the $edit object .
I'm guessing we have to look in the form.

Yes. You can even see them simply by viewing source and looking at the name attribute of the form element ( for instance, textarea name="description" -- not "desc"

Not only the the $edit['attribute_name'] must be as the data is stored, the way it is set is critical:

<?php
array(
'title' => '!SimpleTest! test title' . $this->randomName(20),
// ...
)
?>

Not just the obvious !SimpleTest! within exclamation points part, but the words that follow it must be the word test and then very same attribute name, separated only by spaces, and followed by the test data, without a space!

I had been making it more conversational, and written "test term description" -- wrong, it must only be "test description".

As so:

<?php
    $edit = array(
      'name' => '!SimpleTest! test name'. $this->randomName(14),
      'description' => '!SimpleTest! test description'. $this->randomName(102),
    );
?>

One more issue I had, was that the drupalPost call here redirects not to the term view but to the main taxonomy administration page. Printing out all of the $this testing object showed me that:

<?php
    $this->drupalPost('admin/content/taxonomy/edit/term/'. $term[0]->tid, $edit, t('Save'));
drupal_set_message($edit['description']);
drupal_set_message('<pre>'. var_export($this,TRUE) .'</pre>');
    $this->drupalGet('')
    $this->assertText($edit['name'], t('The randomly generated term name is present.'));
    $this->assertText($edit['description'], t('The randomly generated term description is present.'));
?>

So here is the finished test case, a good bit cleaner and more correct in some areas than the node edit test it was modeled on:

<?php
class TermEditTestCase extends DrupalWebTestCase {
  function getInfo() {
    return array(
      'name'  => 'Term edit test',
      'description'  => t('Ensure terms can be edited from administration page
       and that term name and description are saved.  (Advanced options are not
       tested.)'),
      'group' => t('Taxonomy'));
  }
  function testTermEdit() {

    /* Prepare settings */
    // @TODO change or remove:  variable_set('node_options_page', array('status', 'promote'));
    /* Prepare a user to do the stuff */
    $web_user = $this->drupalCreateUser(array('administer taxonomy'));
    $this->drupalLogin($web_user);
    $edit = array(
      'name' => '!SimpleTest! test name'. $this->randomName(12),
      'description' => '!SimpleTest! test description'. $this->randomName(100),
    );

    // Create the term to edit (adding to the default 'Tags' vocabulary)
    $this->drupalPost('admin/content/taxonomy/1/add/term', $edit, t('Save'));

    $term = taxonomy_get_term_by_name($edit['name']);
    $this->assertNotNull($term, t('Term found in database'));

    // Submitting a term takes us to the add page; we need the List page
    $this->clickLink(t('List'));

    // Test edit link as accessed from Taxonomy administration pages
    // because Simpletest creates its own database when running tests
    // we know the first edit link found on the listing page is to our term
    $this->clickLink(t('edit'));

    $this->assertText($edit['name'], t('The randomly generated term name is present.'));
    $this->assertText($edit['description'], t('The randomly generated term description is present.'));

    $edit = array(
      'name' => '!SimpleTest! test name'. $this->randomName(14),
      'description' => '!SimpleTest! test description'. $this->randomName(102),
    );

    // edit the term
    $this->drupalPost('admin/content/taxonomy/edit/term/'. $term[0]->tid, $edit, t('Save'));

    // view the term and check that it is correct
    $this->drupalGet('taxonomy/term/1');
    $this->assertText($edit['name'], t('The randomly generated term name is present.'));
    $this->assertText($edit['description'], t('The randomly generated term description is present.'));
  }
}
?>

Now to submit this enhanced version of the patch!

Reverse an associated patch that I'd had applied, that we don't want merging with the patch we are replacing:

Ebony-II:drupal-7 ben$ patch -p0 -R < term_edit.patch

Now create a new patch, which will be suitable to replace "taxonomy_list_plus_test_0.patch":

cvs diff -up modules/taxonomy/taxonomy.test > taxonomy_list_plus_admin_and_edit_term_tests.patch

Corrected the patch to make the info statement cleaner and remove some comment cruft "

<?php
    /* Prepare settings */
    // @TODO change or remove:  variable_set('node_options_page', array('status', 'promote'));
?>

"

Ran the exact same patch generating code again, from drupal's root directory:

cvs diff -up modules/taxonomy/taxonomy.test > taxonomy_list_plus_admin_and_edit_term_tests.patch

Those of you paying attention, unlike myself, may have noticed that the patch we were extending involved multiple files. And in fact files in the node module as well as the taxonomy module. So the command needed to create a patch which affects multiple files is like this:

cvs diff -uRp modules/taxonomy > taxonomy_list_plus_admin_and_edit_term_tests.patch
cvs diff: Diffing modules/taxonomy

But we need more, as just noted. This is a multiple module patch! (Actually, it shouldn't have been, it turned out.)

I had to check out a fresh Drupal head and wipe the database to test my own patch, incidentally.

So just a little more cleanup, and remake the patch:

cvs diff -uRp modules/taxonomy > taxonomy_list_plus_admin_and_edit_term_tests.patch

cvs diff: Diffing modules/taxonomy

But taking a look at the patch created I could see things that weren't quite right... it talked about changes that we did not make.

The moving drop of Drupal must have changed taxonomy module in head since the patch was last run yesterday.

What to do? Update my code to match Drupal head. The basic CVS update command will do this and keep my changes, merging them nicely, if possible. Let's try it!

cvs update
? taxonomy_list_plus_admin_and_edit_term_tests.patch
? sites/default/files
? sites/default/settings.php
cvs update: Updating .
cvs update: Updating database
// ...
cvs update: Updating modules/system
cvs update: Updating modules/taxonomy
M modules/taxonomy/taxonomy.admin.inc
M modules/taxonomy/taxonomy.test
cvs update: Updating modules/throttle
cvs update: Updating modules/tracker
//...

See the exciting merge notes (M)? (That doesn't mean there were differences with Drupal head other than our patch, but it does mean that it could merge in our patch without conflicts.) Now we can create the patch again:

I did CVS update on a recent checkout of Drupal head, and saw a U and P next to the two files that were modified. A U I knew meant update, but did something go wrong with the other file? Nope, CVS status P means patch, it's like a little update!

Note: patches to core are supposed to be made relative to the Drupal root directory, and applied using patch -p0 < patch_name
The key part there is the -p0 (that's a zero)

(So this was wrong-- this patch is for drupal core so relative to drupal root, so i should put the patch in the root directory)
cd drupal-7/modules/taxonomy/
wget http://drupal.org/files/issues/term_edit.patch

Testing Reference:

(I didn't actually read this, found it late, but should have, and maybe should contribute to it:)
HOWTO: Submit tests with your patch
http://groups.drupal.org/node/11020

@TODO:
we are going to deprecate this link:

admin/content/taxonomy/edit/term

@TODO:
Go back and fix node.test to use t(), at least

@TODO other thoughts:
on-off permissions for whether you can add terms, probably per vocabulary

edit term permission without administer taxonomy -- perhaps

possible mass operations

Resolution

Searched words: 
contributing to core

Comments

For more on testing a patch... in 6 easy steps

(OK, maybe not so easy. Maybe the word I mean is rigorous.)

http://webchick.net/6-pass-patch-reviews

... and many more development and patch resources.

Post new comment

The content of this field is kept private and will not be shown publicly.
  • You may post code using <code>...</code> (generic) or <?php ... ?> (highlighted PHP) tags.
  • You can use Markdown syntax to format and style the text. Also see Markdown Extra for tables, footnotes, and more.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <img> <blockquote> <small> <h2> <h3> <h4> <h5> <h6> <sub> <sup> <p> <br> <strike> <table> <tr> <td> <thead> <th> <tbody> <tt> <output>
  • Lines and paragraphs break automatically.

More information about formatting options

By submitting this form, you accept the Mollom privacy policy.