CSC/ECE 517 Fall 2009/wiki3 15 assertions

From Expertiza_Wiki
Jump to navigation Jump to search

Programming with assertions

Assertions are predicates or statements with expressions which the programmer thinks should be true at the time of execution. Assertions are a powerful way of detecting bugs in programs.

Overview

Every software product goes though a Software Process or more commonly known as Software Development Cycle. The various stages in the cycle are:

Waterfall model of Software Development Cycle. Image courtesy: Pressman, Roger S. Software Engineering: A Practitioner's Approach", 2001 Fifth Ed.
  • Planning
  • Design
  • Development
  • Testing
  • Release and Maintenance

It is impossible for a programmer to make any program a hundred percent bug free. Even worse, there maybe subtle things which the programmer assumes are always true but may not be so in many corner cases. Debugging is an integral part of the Software Process. Bugs can crop up anytime due to various test cases formulated all through these aforementioned stages. To be sure that whatever the programmer assumes is true, assertions can be set up in the program. If ever, the condition evaluates to be false, the program terminates with an assertion. Another way to put it is that assertions are a way to show the programmer that things cannot be taken for granted by the latter. Assertions can be thought of as medium of validating the correctness of modules.

Purpose

The various uses of assertions are:

  • Validates correctness of (sub)program
  • Supports programming by contract (documentation)
  • Helps in debugging
  • Exception handling by integrating assertion failures to be detected dynamically

Assertions can be used by the programmer to make sure that wrong runtime assumptions (by the programmer) can be caught.

Types of assertions

Assertions can be categorized primarily as follows:

  • Pre-conditions
  • Post-conditions
  • Class invariants
  • Invariants (Control-flow, Loop etc.)
  • Loop variants

As Dr.Gehringer mentions, the first three classes are discussed in class and will not be a focus of this wiki. Instead, this will concentrate on the other types of assertions, viz., Control flow, Loop invariants and Loop variants[1]. Invariants are those conditions with a precondition as well as post condition. A loop invariant is an expression for a variable which remains constant through-out the loop. On the other hand, control-flow invariants are those cases which specify that the control should never reach to that part of code. Consider the following code[1]:

for (....) {

  if (...)
      return x;
  else if (...)
      return y;
 
 /* Control should never reach here */

}

Now observe how control-flow invariance can be validated by assertions:

for (....) {

  if (...)
      return x;
  else if (...)
      return y;
 
  assert false

}

They are also a very useful tool to detect logical errors. For e.g consider the following code[1]:

switch(scase) {

  case 1: ....
  case 2: ....
  default: assert false;

}

This code fragment clearly says that the programmer assumes that the variable scase will take either of the listed values. If the variables reaches the default segment, then there is a logical error in the program.

Numerous other run-time checks can be made. The following example illustrates that[1]:

if (threads % 4 == 0) {....}

else if (threads % 4 == 1) {....}

else if (threads % 4 == 2) {....}

else {

  // we know that it has to be 3
  assert(threads % 4 == 3);
  ....

}

This method is called as asserting an Internal invariant. The latter means that even though its obvious that the result would be 3, we eliminate any logical errors by asserting that.

Programming languages supporting native assertions

The following programming languages support assertions natively[2]:

  • Cobra
  • D
  • Eiffel
  • Fortress
  • Lisaac
  • Nice
  • Oxygene (formerly Chrome)
  • Sather
  • SPARK, via static analysis of Ada programs
  • Spec#

Advantages and disadvantages of assertions

Some programming languages natively support assertions (mentioned above) while some support through add-ins (like C/C++, C#, Java, Javascript etc.). However, most of the above listed languages are high level languages with less popularity. Hence the need for supporting assertions in the commonly used languages is important.

Advantages

There are many advantages of assertions. Some of them are enumerated here[3]:

  • they remove the need for passive assertions (in the form of comments) and instead actively assert the condition and fail if false during run-time: comments may outlive the code and may not hold for the new code. They act like passive assertions, so its the programmer who has to take care of the condition. In case of assert statements, they enforce the conditions and never outlive the code. If the code changes, the assert statement has to change too, for the program to execute. They thus act as active snippets of comments.
  • they make you more confident of your code: The programmer makes a certain assumptions during the coding phase. Assertions validate the programmer's assumptions. If any of them turns out to be false, code without assertions may execute and show unexpected behavior. Assertions avoid such situations and make the code less buggy.
  • they help refactor the code: Whenever somebody's refactoring the code, leaving the assertions in place (or modifying them accordingly) ensures that the logic is not flawed and the refactor was actually done (without altering the logic). It is a way to validate the process of refactoring. The same hold true for optimization. Assertions ensure that while in the process, the program logic was not altered.
  • they act as embedded tests: A very good example is the development of Program 1: MeToo. A lot of assertions were used in the unit tests. Assertions are like small tests embedded within the code. They simplify the testing of the code.
  • they help in debugging: When a program has assertions, it is easy to narrow down the error since the programmer now knows that if there is an assertion error then the problem must be somewhere in the vicinity of the assertion. They thus help in efficient debugging.
  • they support Programming by Contract: Assertions help establish a relationship between a class and its consumers. It defines what rights does an element have and what is it supposed or capable of doing.

Disadvantages

There are some disadvantages to using assertions too. However, the advantages outnumber the disadvantages. Some of the disadvantages are listed below:

  • the biggest disadvantage is probably the speed: Code with a lot of assertions is slower than one without assertions. However, it can be argued that during development its the accuracy and correctness that is utmost important and thus speed can be sacrificed at the cost of correctness/accuracy[3].
  • assertions can be tricky to write: Unless the programmer puts down his/her assumptions in the form of assertions accurately, they serve no purpose.
  • assertions can cause dependency problems: A class may be heavily dependent on every assert class in it. Thus extension maybe a problem
  • stimulus must make use of assertions: Extensive and exhaustive testing is still required. The test-suite (or the stimulus) must make use of all the assertions for them to show their benefit. If the stimulus never tests some special corner cases, those assertions will never fire up to trigger an error[5].

When NOT to use assertions

If used incorrectly assertions can be a bane and can significantly alter the program flow[4]. Consider the following code:

assert((var = a*3*0.45*i) != 0); var1 = var2/var;

The problem with the above code is that the expression is evaluated inside the assert statement. Most languages have a feature to disable assertions. What if assertions are disabled? var would never be correctly evaluated and some garbage value maybe passed on to the next statement. If var is passed on as '0', then the program would generate a 'Divide by zero' exception. A fix to that problem would be to separate the expression evaluation and then assert. The following code shows the fix:

var = (a*3*0.45*i); assert(var != 0); var1 = var2/var;

Thus one must be careful when using the assert statements.

References

  1. http://java.sun.com/j2se/1.4.2/docs/guide/lang/assert.html
  2. http://en.wikipedia.org/wiki/Design_by_contract
  3. http://www.stanford.edu/~pgbovine/programming-with-asserts.htm
  4. http://courses.csail.mit.edu/6.170/old-www/2002-Fall/lectures/lecture-10.pdf
  5. http://www.esperan.com/pdf/esperan_introduction_to_psl.pdf