Unit-testing in C for a noob?

Not about OpenMW? Just about Morrowind in general? Have some random babble? Kindly direct it here.
Post Reply
HiPhish
Posts: 323
Joined: 02 Jul 2012, 08:36

Unit-testing in C for a noob?

Post by HiPhish »

Hey guys,

I have been writing a C program recently as an exercise for myself- it's an implementation of Newton's method that uses a compiler to turn the user's input from a string representing a mathematical function into a bytecode representing said function and then runs it on a virtual machine. All written by myself using only the C standard library.

I think I have it now, but testing a program by entering a function and seeing whether things blow up is tedious and not very accurate, so I wanted to learn unit-testing. My problem is that every resource I can find only describes using a unit-testing framework. Can anyone explain to me how to do it myself or point me to an article or even a book?

Here is how the project is structured: there is the main source file (newton.c) the contains the main function that ties things together. Then there are different modules in their own sub-directories. Some modules are dependent on others, but for the most part every module is its own thing. The compiler and the virtual machine need to know about the bytecode but they know nothing of each other. Here is the directory structure used:

Code: Select all

Source
|
|- newton.h (common constants like PI)
|- newton.c (main function)
|
|- compiler
|  |-  compiler.h
|  |-  compiler.c
|
|- function_deriver
|  |- function_deriver.h
|  |- function_deriver.c
|
|- lexer
|  |- lexer.h
|  |- lexer.c
|
|- method (Newton's method)
|  |- method.h
|  |- method.c
|
|- parser
|  |- parser.h
|  |- parser.c
|
|- syntax_node
|  |- syntax_node.h
|  |- syntax_node.c
|
|- syntax_node_stack
|  |- syntax_node_stack.h
|  |- syntax_node_stack.c
|
|- virtual_machine
   |- opcodes.h
   |- vm_code.h
   |- vm_code.c
   |- vm.h
   |- vm.c
Basically the idea is that every module is in its own subdirectory. There is a header file that contains the functions that will be exposed to the program using the module and there are functions internal to the module defined as static in the source file.

So how can I test the vm_code units? For the public functions it's simple:

Code: Select all

/** @file Tests/virtual_machine/test_vm_code.c */
#include "../../Source/virtual_machine/vm_code.h"

// this will be exposed to the function running all the tests
int test_vm_code(void) {
	int success = 0;
	if (success += test_vm_code_new()   != 0) {goto end;}
	if (success += test_vm_code_copy()  != 0) {goto end;}
	if (success += test_vm_code_clear() != 0) {goto end;}

end:
	return success;
}

static int test_vm_code_new(void);
static int test_vm_code_copy(void);
static int test_vm_code_clear(void);

static int test_vm_code_new(void) {
	VMCode code = test_vm_code_new();
	return (code.length == 0 && code.code == NULL) : 0 : 1;
}

/* and so on */
Looks fine, but here is the problem: how do I test the units that are not exposed outside the module, i.e. the actually interesting parts? One idea is to copy-paste the source file into my test source file:

Code: Select all

#include "../../Source/virtual_machine/vm_code.c"
But now all the includes in my source file are messed up because the test file is not where the source is.

So my problem is, how do I hook up my source code that is meant to be one program, to the test code, which will compile into an entirely different program?

Why am I not just using a unit testing framework like everyone else? As I said above, this program is mostly an exercise to myself. I believe before you start using the power tools you should do it yourself first at a smaller scale. Before using autotools one should know how a makefile is built and before using a makefile on should first know how to invoke the compiler. All the resources I could find on unit-testing frameworks were written as if the reader was already expected to know how a unit testing framework works. That kind of defeats the point of it, doesn't it?
User avatar
sirherrbatka
Posts: 2159
Joined: 07 Aug 2011, 17:21

Re: Unit-testing in C for a noob?

Post by sirherrbatka »

Looks fine, but here is the problem: how do I test the units that are not exposed outside the module, i.e. the actually interesting parts?
Well, you just discovered the dire need for dependency injection/inversion of control.

Simply try to split initialization to smaller parts.
How do I test the units that are not exposed outside the module, i.e. the actually interesting parts?
Well, OO crowd would say that that you should not hide anything. In case of OO that would be: no private methods (if you need private method, you need public method in the utility class).

Functional people obviously have easy time when it comes to testing.
wheybags
Posts: 207
Joined: 21 Dec 2012, 19:41

Re: Unit-testing in C for a noob?

Post by wheybags »

could do conditional privacy with macros. eg, in your header:

Code: Select all

#ifndef PRIVATE
#define PRIVATE private

class foo
{
    public:
        int x;
    PRIVATE:
        int some_private_member;
};
then in your unit test file:

Code: Select all

#define PRIVATE public
#include "header.h"

// tests here can access some_private_member
EDIT: just remembered this was for c, but you get the idea.
c equivalent would be something more like:

Code: Select all


int foo();

#ifdef TESTING

int private_func();

#endif

HiPhish
Posts: 323
Joined: 02 Jul 2012, 08:36

Re: Unit-testing in C for a noob?

Post by HiPhish »

Do I have to introduce testing code into the source? I wanted to keep the source clean of it. I don't use a header file for the private functions, they are declared and defined in the same file. Here is the module for the virtual machine, I have cut out the comments and function bodies:

Code: Select all

/* Public header of the module */
#ifndef NEWTON_VM_H
#define NEWTON_VM_H

#include <stdint.h>
#include "../newton.h"
#include "vm_code.h"

/** Structure of a virtual machine to run the bytecode of an arithmetic expression. */
typedef struct virtual_machine {
	VMCode code; /**< Compiled bytecode of an arithmetic expression. */
	/* Registers */
	double reg_x; /**< Value of the variable for the arithmetic expression. */
	/** Stack for intermediate values. */
	struct {
		/** Fixed-size stack array. */
		double stack[MAX_ARITY];
		/** Index of the stack top. */
		size_t  stack_top;
	};
} VirtualMachine; 

VMCode machine_load_code(VirtualMachine *machine, VMCode code);

int machine_execute(VirtualMachine *machine, double *result);

#endif /* NEWTON_VM_H */

Code: Select all

/* Source file with private function */
#include <stdlib.h>
#include <math.h>
#include <assert.h>
#include "../newton.h"
#include "vm.h"
#include "opcodes.h"

/** Pops a value off the stack and returns it. */
static double pop(VirtualMachine *vm);

VMCode machine_load_code(VirtualMachine *machine, VMCode code) {
	/* ... */
}

int machine_execute(VirtualMachine *machine, double *result) {
	/* ... */
}

static double pop(VirtualMachine *vm) {
	/* ... */
}
Testing the code loading and code execution is easy, but popping a value of the stack is not. There is nothing explicitly private about the function, except for the fact that it is not exposed.
Post Reply