This tutorial will walk you through the process of writing a Snoopy test. The test we are going to write will prohibit the use of Perl style hash comments (#) in PHP code.
There are a number of sub-directories in the main Snoopy test directory (/path/to/snoopy/tests). Each sub-directory represents a category of tests. These sub-directories are used to group similar tests together.
Of course, if you feel that your test does not fit within an existing category, you can create a new one.
Our test prohibits hash comments within the source, therefore it belongs in the existing commenting category. All testing files we create will be placed directly in this directory.
After you choose a category for your test, you need to create three files. The name of each file is based on a unique name you must give to your new test. In this case, we have decided to call our new test hash_comments. The three files you need to create are:
An easy way to start developing a new Snoopy test is to take the files from an existing test and change the PHP code and XML as necessary.
When Snoopy initialises, it asks each of the tests what tokens they are interested in by calling the register_
Our test is interested in comments, so we want to listen for T_COMMENT tokens.
You can find the full list of parser tokens and their syntax in the PHP documentation, Appendix Q: List of Parser Tokens.
The following function should be placed in the hash_comments.sniff file you created earlier. The function returns an array of tokens that the test wants to register to process. In our case, we return an array with a single element.
<?php
/**
* Registers that this test wants to listen to T_COMMENT tokens
*
* @return Array() a list of tokens
* @access public
*/
function register_hash_comments()
{
return Array('T_COMMENT');
}//end register_hash_comments()
?>
Whenever a T_COMMENT token is encountered in a tested PHP file, the process function of our test will be called. This is where the actual checking of the coding standard occurs.
Similar to the register function, the process function of a test is named in the format process_
The first argument ($stack_ptr) is the index in the $tokens array where Snoopy found the token. Note that if you are listening for more than one token type you will have to determine what the encountered token type is.
The second argument ($tokens) is an array of every token that exists in the file currently being processed. Each entry in $tokens is an associative array which contains information about a particular token in the following format:
1:<?php 2: Array( 3: 0 => 351, 4: 1 => '#hash comment', 5: 2 => 'T_COMMENT', 6: 'line' => 27, 7: 'level' => 1, 8: 'level_conditions' => Array(12), 9: 'start_column' => 0, 10: 'end_column' => 13, 11: 'length' => 13, 12: ); 13:?>
The descriptions of the array indices are as follows:
For our test, we want to raise an error when a hash style comment is encountered. The following function checks for hash style comments and raises an error. The function should be placed in the hash_comments.sniff file you created earlier. The function does not return a value, but raises an error for each coding standard violation it encounters.
<?php
/**
* Processes T_COMMENT tokens to prohibit the use of
* perl style hash comments
*
* @param int $stack_ptr the position of the found token
* @param Array $tokens the current file's tokens
*
* @return void
* @access public
*/
function process_hash_comments($stack_ptr, $tokens)
{
// verify that the token is a comment
if ($tokens[$stack_ptr][0] != T_COMMENT) return;
// is the first character of the comment a '#' ?
if (substr($tokens[$stack_ptr][1], 0, 1) == '#') {
// raise an appropriate error message
$message = 'Hash comments not allowed, please use // comments instead.';
$GLOBALS['SNOOPY']->throwError($message, $stack_ptr);
}
}//end process_hash_comments()
?>
The throwError() method in /path/to/snoopy/snoopy.inc records all the errors thrown during a test run and displays then upon completion. Always use the throwError() method to generate Snoopy errors. Do not output errors to the screen directly.
Snoopy tests should only report errors for the coding standard they are checking. For example, in our hash comments test we may be tempted to test that comments are indented correctly, or that multi-line comments are defined correctly. However, the checking of these standards should be left to another test to ensure the modularity of Snoopy.
Now that we have completed the code to enforce the coding standard, we need to create some scenarios to verify that the test is working correctly. This is known as self-testing in Snoopy. These self-test scenarios must be placed in a file called
Place the following PHP code into the hash_comments.test file you created earlier.
1:<?php 2: 3:# Illegal comment type 4: 5:// Legal comment type 6: 7:/* 8:* Legal comment type 9:*/ 10: 11:$hash_comment_test_errors = Array( 12: 3 => 1, 13: ); 14:?>
You can see that line 3 contains a hash comment, which is illegal as per our new coding standard. We also have two legal comments so that we can verify that no errors are generated for valid commenting styles. Lastly, we have defined an array called $hash_comments_test_errors to let Snoopy know which lines in this file contain errors. This key of the array indicates the line number that contains errors and the value of the array indicates how many errors will be found on that line. In this case, we tell Snoopy that line 3 contains 1 error. Again, these are just errors that our new Snoopy test will generate and do not include coding standard violations checked by other tests.
With this file completed, we can verify that our Snoopy test is enforcing the new standard correctly by specifying the self-test option to Snoopy. See the Snoopy usage documentation for information on how to self-test Snoopy.
If all goes well, Snoopy will not output anything after running this command. If the self-testing fails, Snoopy will warn you that it encountered unexpected errors. These errors may be:
Try changing the contents of the hash_comments.test file to the following code:
1:<?php 2: 3:# Illegal comment type 4: 5:// Legal comment type 6: 7:# another illegal comment 8: 9:/* 10:* Legal comment type 11:*/ 12: 13:$hash_comment_test_errors = Array( 14: 3 => 2, 15: 5 => 1, 16: ); 17:?>
Run a Snoopy self-test again and see what errors are generated. Snoopy will report that line 3 is not producing enough errors, that line 5 is not reporting any errors and that line 7 is reporting errors when it shouldn't be.
The last thing we need to do is document our new coding standard so that Snoopy can generate PDF documentation for it.
Place the following XML into the hash_comments.standard file you created ealier.
<documentation title="Hash Comments" code="CM01" version="1.0"> <standard> <![CDATA[ All comments should be defined with '//' and not with '#'. ]]> </standard> <code_comparison> <code title="Valid: correct comment"> <![CDATA[ <b>//</b> This is a comment ]]> </code> <code title="Invalid: hash comment"> <![CDATA[ <b>#</b> This is a comment ]]> </code> </code_comparison> <standard> <![CDATA[ You can even use '//' style comments over multiple lines. ]]> </standard> <code> <![CDATA[ // Here is a long comment that spans over // multiple lines and uses the correct // commenting style ]]> </code> </documentation>
The title of the standard will be displayed on all documentation. It is basically the friendly name of the coding standard.
The code is a unique 4 character code that you can use to refer to this standard. Here, we prefixed the code with CM to indicate it is a commenting standard and numbered it 01 because it is the first test we have written. The code can be anything you like. It can also be left empty.
The version is the current version number of the test. As you make changes to the test or its documentation, increment the major or minor version number to keep track of how many revisions you have made to it.
Inside the documentation tag with have three other types of tags we can use, standard, code and code_comparison. The standard tag is used to print normal content. The code tag is used to print a code box, for code samples. The code_comparison box is used to print two code boxes side by side to compare good and bad code samples.
You can make text stand out in standard blocks by using <i> and <b> tags. You can also use <b> tags within code blocks. Snoopy will highlight the selected text to draw attention to it. This is especially useful in code_comparison blocks.
The last thing we need to do is update Snoopy's table of contents so that out new test appears in the coding standards PDF. Edit the file /path/to/snoopy/tests/toc.xml and add the following tag into the table of contents:
<category name="My Snoopy Standards"> <standard name="hash_comments" path="commenting" /> </category>
This will create a new category of standards called My Snoopy Standards and add our new Hash Comments coding standard to that section of the PDF. The path attribute of the XML tag tells Snoopy which directory your test is in.
You can play around with the ordering of sections in the toc.xml file and you can move standards between categories. You can also add your new test to an existing category, the Commenting category for example.
Now that the standard is written, you can generate the coding standards PDF file with Snoopy and see your good work (see image below). See the Snoopy usage documentation for information on how to generate the PDF file.
|
| This is what your new coding standard should look like after you have generated the coding standards PDF file. Click the image for a larger version. |
Your first Snoopy test is written and ready to go. The only thing left to do now is start testing some code with it. The Snoopy usage documentation will help you use Snoopy to find code that violates your new coding standard.