• No results found

Starting a C Module

In this chapter, I’ll lead you through some of the first steps you’ll take when test-driving a new module in C. In the next chapter, we’ll be in full stride as we complete the module. Starting in this chapter and continued throughout this book, we will see whether we can realize Dijkstra’s vision of not introducing bugs.1 The tool we will use is TDD.

3.1 Elements of a Testable C Module

The examples in the book will use the idea of a module. For our pur-poses, a module is a self-contained part of a system that has a well-defined interface. This does not say how big a module can get. In this book, we’ll use small modules; the module examples will coincide with compilation units, although in the wild, not all modules consist of a single compilation unit. You will see that modularity is needed to make testable code. You will also see that modular design is a natural out-come of TDD.

Testability impacts design significantly and positively. In creating mod-ular C, we will draw upon the idea of an abstract data type. Bar-bara Liskov defines ADTs in her Programming with Abstract Data Types [Lis74] as follows: “An abstract data type is defined indirectly, only by the operations that may be performed on it and by mathematical con-straints on the effects (and possibly cost) of those operations.”

In an ADT, a module’s data is treated as private; it is encapsulated.

There are a couple modularity options we can employ for encapsulating

1. The Edsger Dijkstra quote is from The Humble Programmer [Dij72].

Download from Wow! eBook <www.wowebook.com>

ELEMENTS OF ATESTABLEC MODULE 52

a module’s data. The first choice is to hide data using static file scope variables in the .c file, giving access to the functions in the compila-tion unit. The data is accessible only indirectly through the module’s public interface, which is defined in the .hfile as a set of function pro-totypes. This approach works for a module that has a single set of data to manage, something I call a single-instance module.

When a module has to manage different sets of data for different clients, we can use the multiple-instance module. With a multiple-instance mod-ule, structures must be initialized and passed back to a client holding their context. Here is where ADTs come into play. You can declare a typedefof a forward declaredstructin a header file like this:

typedef struct CircularBufferStruct * CircularBuffer;

The compiler is happy to allow pointers to incomplete types to be passed around as long as no code dereferences the pointer. Inside the .cfile for CircularBuffer, the struct elements can be defined, effectively hiding the data so that only the module whose responsibility is the integrity of that structure can manipulate it. If you are familiar with the POSIXpthread library, it uses this technique.FILE, from Unix, is another example of an ADT.

When doing TDD to create modular C, we will use these files and con-ventions:

• The header file defines the module’s interface. For single-instance modules, the header file is made up of function prototypes. For abstract data types, in addition to function prototypes, a typedef is created for a pointer to a forward declared struct. Again, hiding thestructhides the data details of the module.

• The source file contains the implementation of the interface. It also includes any needed private helper functions and hidden data.

The module implementation manages the integrity of the module’s data. For ADTs, the forward declared struct members are defined in the source file.

• The test file holds the test cases, keeping test code and production code separate. Each module has at least one test file usually con-taining only one, but sometimes a few, test groups. Test groups are organized around data common to all the tests in the group.

When the setup needs of some test cases are significantly differ-ent from others, we’ll use multiple test groups and maybe multiple test files.

WHATDOES ANLED DRIVERDO? 53

• Module initialization and cleanup functions. Every module that manages hidden data should have initialization and cleanup func-tions. ADTs certainly need them with their totally hidden inter-nals. C++ builds this idea into the language with constructors and destructors. By convention, I’ll makeCreateandDestroyfunctions for each module. Modules made up of stand-alone functions, like strlen( ) andsprintf( ), that keep no internal state won’t need initial-ization and cleanup.

Following these practices and conventions makes code easier to test and easier to read and evolve. It’s not impossible to test code that follows the data structure/function call free-for-all approach—it’s just harder. In the first example, I’ll use the single-instance module while test-driving an LED driver. We’ll use abstract data types later.

3.2 What Does an LED Driver Do?

Let’s say that our system uses LEDs to communicate status to the users or developers of the system. We are going to need a driver for the LEDs.

Here’s what we know about the LED driver requirements:

• The LED driver controls 16 two-state LEDs.

• The driver can turn on or off any individual LED without affecting the others.

• The driver can turn all LEDs on or off with a single interface call.

• The user of the driver can query the state of any LED.

• At power-on, the hardware default is for LEDs to be latched on.

They must be turned off by the software.

• LEDs are memory-mapped to a 16-bit word (at an address to be determined).

• A 1 in a bit position lights the corresponding LED; 0 turns it off.

• The least significant bit corresponds to LED 1; the most significant bit corresponds to LED 16.

The first four goals are focused on what the LED driver is supposed to do. Goals 5 to 8 describe how the driver will interact with the hardware.

In addition to these requirements, there’s a design goal: make the driver testable off the target hardware. There is just one bank of LEDs in the target system, so we’ll use the single-instance design model.

Before we start, let’s figure out what tests we need.

Report erratum this copy is (P1.0 printing, April, 2011)

Download from Wow! eBook <www.wowebook.com>

WRITE A TESTLIST 54