Easy Extend



              

       Coverage



   





            Author: Kay Schluehr

            Date of creation: 2006-05-04
            Last change: 2008-03-05
            Version: 2/3 - requires EasyExtend 3.0



1  Description

The coverage langlet module is langlet created for code coverage purposes. The basic idea of the coverage langlet is to compile a function call into each code block of the analyzed module. If the code block is entered the function will be called and the call will be notified. This differs from approaches used by the popular coverage.py module of Ned Batchelder who uses Pythons builtin tracing function sys.settrace.

1.1 Terminology

For each code block a Sensor object will be created and inserted into the block. You can imagine a bunch of sensors spreaded all over the code-blocks and signaling their presence to an external Monitor object. The Monitor holds a reference to each sensor and draws an image of their activation after execution of the analyzed module. The ratio between all sensors distributed over the code against those that were activated is a measure of code coverage. Currently only statement coverage is implemented. It remains however an important incentive to advance beyond statement coverage by using code transformations - the proper domain of EasyExtend.

2. Use


usage: python fiber.py [option] ... file
Options:
-o FILE : redirect output from stdout into report output FILE.
-p      : pattern used to describe names of modules imported using the
          coverage importer. pattern is a quoted regular expression.   
-d      : deactivate default pattern ( see explanation below ) 
-e      : erase all pycv files in the directory of the main module and below
file : script file to start with

The -p option defines a selection among modules using a regular expression. As default pattern the expression '(?P<test>test\_(?P<mod_name>\w+))' is used. This pattern extracts the group of strings ("test_module", "module") from the string "test_module" where "module" is a module name. It can be deactivated using the -d option.

Examples:
 (1) python run_coverage.py test\test_demo.py
 (2) python run_coverage.py -d language.py

In (1) the module test_all.py will be covered but also each module test_xxx.py that is imported during execution of test_all.py as well as each module xxx.py.

In (2) we deactivate the default import rules of (1) and cover language.py. The language module imports the eetransformer module that will be covered as well.


3 Statement tests

3.1 Symbols

   ">"   --  marks a location where a sensor has been inserted
   "!"   --  marks a location where a sensor did not respond

3.2 Omissions


The following example shows the result of executing the command  python run_coverage.py test_demo.py.

Each new codeblock is marked by a sensor. One exception is made with blocks

            if __name__ == '__main__':
         BLOCK

not being in the __main__ module. They will never get called and the ratio between activated/deactivated sensors does not provide valuable information.

Each measurement on a module is terminated by a status message. If more than one module is involved a summary is provided.


     _____
    /  __ \
    | /  \/ _____   _____ _ __ __ _  __ _  ___
    | |    / _ \ \ / / _ \ '__/ _` |/ _` |/ _ \
    | \__/\ (_) \ V /  __/ | | (_| | (_| |  __/
     \____/\___/ \_/ \___|_|  \__,_|\__, |\___|
                                     __/ |
                                    |___/


----------------------------------------------------------.
 Coverage : c:\Python24\Fibers\pytools\test\test_demo.py  |
---------------------------------------------------------------------------------------.
0
1         from demo import Demo
2        
3        
4 >       if __name__ == '__main__':
5             Demo(0,-1)
6             d = Demo(1,0)
7 >  !        if 0:                     # this sensor does nothing for obvious reasons
8                 d.f()

---------------------------------------------------------.
Status:                                                  |
  2 sensors created                                      |
  1 sensors did not respond                              |
Coverage: 50%                                            |
---------------------------------------------------------------------------------------'

-----------------------------------------------------.
 Coverage : c:\Python24\Fibers\pytools\test\demo.py  |
---------------------------------------------------------------------------------------.
00
01 >       class Demo:
02 >           def __init__(self,a,b):
03 >               if a or not b:
04                     print "Demo Result => a +:",a
05 >               else:
06                     print "Demo Result =>  a -:",a
07 >               if b<0:
08 >                   while 1:
09 >   !                   if b<-1:
10                             return
11                         b+=1
12 >                       if b==0:
13                             break
14                             a-5        # ...does not detect all unreachables!
15 >   !                   b-2            # may not be reached
16 >               else:
17                     print "Demo Result =>  b:",b
18
19 >   !       def f(self):
20                 return 0
21
22
23         if __name__ == '__main__':
24             Demo(0,-1)
25             d = Demo(1,0)
26             if 0:
27                 d.f()

----------------------------------------------------.
Status:                                             |
  10 sensors created                                |
  3 sensors did not respond                         |
Coverage: 70%                                       |
---------------------------------------------------------------------------------------'

========================================================================================
Summary:
  12 sensors created
  4 sensors did not respond
Coverage: 75%
========================================================================================


4  Expression coverage

4.1 Symbols

   ">"   --  marks a location where a sensor has been inserted
   "!"   --  marks a location in an expression where a sensor did not respond
   "+"   --  marks a location in an expression where a sensor responded

4.2 Coverage of boolean operations

With statement coverage we do not cover boolean operations. Let's modify the __init__ method of the Demo class in the following way:

07               if b<0 or a+b>7:
08                   x = 0
09                   y = 9
10               else:
11                   print b

With statement coverage we do not know which branch of the condition is executed. If b<0 is always true for any test the alternative branch defined by a+b>7 will never be checked.

We can wrap a sensor around any of the conditionals. The idea is to use an identity function with a side effect:

   def measure( expr ):
       monitor.send(sensor_id)
       return expr
           

4.1  Notation

If the second condition in the or-test is never activated but the first one following pattern will be displayed

07 >  [+!]   if b<0 or a+b>7:
08               x = 0
09               y = 9
10 > !       else:
11               print b