0

In an 8bit mcu, I wrote a piece of code of a state machine using a function pointer static void (*state)(void);.

Inside the same compilation (let's say sm.c) unit I implement all the available states.

static void state_foo(void)
{
    //execute code
    //check results and change state
    //state = new_state
}

static void state_bar(void)
{
    //execute code
    //check results and change state
    //state = new_state
}

The state pointer is dereferenced inside another function in order to be able to call its current state.

void run(void)
{
    *state();
}

sm.h

#ifndef _SM_H_
#define _SM_H_
void run(void);
#endif

And finally, the function run() is called inside main().

#include "sm.h"

int main(void)
{
    //other functions
    while(true) // super loop
    {
        run()
    }
}

So, apart from the run() function, outside of the module, I do not have access to any of the globals inside the compilation unit of the state machine.

How can I unit test simple state machines like that?
Inside each state I call functions from another modules that do something and check for results for example check for arrays' values, statuses etc... The state changes upon those decisions.

I can isolate the other modules that get called inside my state machine module. But how can I check for the results without having to add multiple printf functions inside each state? I was thinking of reconstructing the state signatures so they can return a result int that corresponds to each of the available states so the main() function be able to know which state is about to get invoked next etc...

Any ideas on that approach?

1
  • 2
    If you're on an 8bitb MCU just do the tried and true switch() loop state machine. There is no good reason to use function pointers, μcs arent just slow PCs Feb 6 at 18:02

1 Answer 1

5

This particular problem isn't necessarily due to C as a language, but to how you structured the program. In order to make something testable, you need two things:

  • The ability to set state
  • The ability to query state

When dealing with simple functions you can easily test because the output is directly related to the input and there are no hidden state variables. For example if you had a function that was something like this:

void* process_state(void* state) {
    // input state determines the output state
}

Then you can easily test the state transitions in code with assertions. Now, this is technically unsafe code with void pointers. Most state machines I've worked with in C and C++ (many moons ago) dealt with constants. Basically the states would be named with macros, but resolve to an int. Inside the function that did the state machine processing would be a giant switch statement.

Your run() function would then be able to wrap the process_state(state) function and do its work.

Alternatively if you are insistent on using a more object oriented state machine like you've described above, you would test your state function's behavior directly to ensure that it transitions appropriately to the next state.

Bottom line: You cannot test code if you cannot provide inputs and verify outputs. Unit tests are typically structured like this:

  • Arrange the initial state so that the test will be successful no matter what order it is executed
  • Execute the function under test providing the values you arranged previously
  • Verify the results of the end state, using assertions so that execution stops with error information

That can't happen when state is completely hidden and you can't provide inputs. That is an untestable function. You can add log messages or write to STDOUT, but those are debugging tools, not testing code.

Not sure what you are using to test your code, but a unit test framework like Check can help you structure your tests to provide meaningful error reporting to help you zero in and fix your problems quickly. They often have tutorials as well.

1
  • Thank you very much! I made a little experiment writing a mini draft of the approach you suggested and indeed it's inevitable to test code without providing inputs and get outputs. I am using googletest as a unit test tool.
    – MrBit
    Feb 6 at 12:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.