welcome everyone we have adopted by it Alexander stiffened about for 18 and 15 she could reason basis these welcomed money from the few the the incident the session only languages and embedded software developers so acquired by microcontrollers mostly unfortunately this is mostly the code Python code there recently reported Michael 1 of our controllers so somehow we get better are before I start the talk like in a bit more about you and your experiences with unit tests so if you wouldn't any units as any language yet please raise your hands so get an overview of the great at most a few move you has written unit tests for C code for we can see that care for about half of you of last question then is to enjoy the experience especially you compare its divided by by the codons that well the good idea perfect had so and maybe I can show you some more fun way to write unit tests for on my you might wonder of but my motivation is

01:20

that and some of it can probably be summed up with this quote here that you see language combined for the of assembly language all the use of use of assembly language so we see you've got control of everything and you can control everything but usually also have to control everything you need to do everything for yourself this little support from the Language and of for testing stuff you probably don't need all this power you're not constrained with resources you don't have that performance requirements that you might have in production in production code so he could actually use a high-level language to make it easier for you to write a test code and don't do everything and low-level language like C. now let's look into that and the more detail you write unit tests for C code with the code and then there's some good things the same language everywhere so that the developers don't need to switch contacts between different languages different styles different and that might also be good for the developer only knows a single language the kind of process if you're working in an embedded environment like I do then you could be able to run your unit tests on the target device or at least on the simulated devised so that if there are bits and pieces of code for example was implemented in some you can also just those the buttons also that

02:43

limited and and in some ways I already told about the limitations of the language offers you so you can only use the constructs which are not as powerful as Python constructs for example some right much more code and included in the language but you also limited by what the framework has to offer you and you look up unit testing frameworks for C code which there are tons of frameworks out there but most of them are very basic they don't offer advanced features that you might be used to when you look at the unit testing frameworks that offer for example for Python code so few frameworks only that from walking for example and in the end you're also limited by what the system has to offer for example we would like to test some cryptographic algorithms in our implementations and of course you can call into open SSL to verify some some calculation but it's not really that easy and it might be nice to do that and Python the it is now maybe we can do

03:48

better than that and prepare you a few examples to show you how unit testing the code with Python would look like so the 1st example is

03:57

most basic thing I could think of that a single function in our seeker that just X 2 integers and returns the result of this is the other part of the public interface that we want to unit test and this then is the the implementation of the function just at the numbers and returns the value and everybody unit test for that it could look like this so as usual with pattern in terms of you've got a test cases class as a container for the test cases finger function there then is test case only 1 here and it's not simple lots in the source codes that have shown for create a model of that and that has not taken to call can call the function that is defined in the module this function returns the result and the concept that the results correct and I don't see any cycle in here and no construct that really do anything with with the secret from before the only mention the thing that you see is that the name of the module parameters for the loss function and this is where all the magic happens so let's look at that the log function here um consists of 3 steps 1st loads the source code from module so to opens with the final opens the header about rates the source code and then it uses the other 5 to build a Python module of the cost of source code there are 3 calls that you need to make them to see if I object for that In the 1st call this called the tells you what interface has to export to all Python code so we we pass in the header file contents that defines public interface we want to test match so we see it by its to generate at the interface for us and then with the 2nd call we need to tell the of i about the implementation of the function so we pass in the source code here and the last step then this this is applied to actually build the model that we want to have so it runs of the component background is module and in the end of the last step we can import that module and return to our test case and that's really all you need from this example actually before now got 3 more examples that all build on this implementation so I'd like to quickly ask whether there are any questions for this example already so that you can better understand the following examples the and so on and so on because you need more than 1 source eurostat that get because if in this example source file and I have some sort of includes and dependencies to other source files and now how do I cope with that I have to local model also in terms of our of his work are you have got some more complex examples of multiple files and external dependencies conclusion that OK and in more questions was continuing with the 2nd example and the 2nd example still rather basic got against single function and call multiple times will just add up all the parameters of the positives and return the current this is the database and this again the implementation so now we've got a global variable used to some of everything the function just X'd it and returns the current value and the unit just now look like this commitment of the more interesting of 3 years does not only 1 and so that I do not have to repeat the slope calling every test is again I is the set of methods to get executed before each test cases run it will load the model for the test cases and then the test case can access the module just before the called function there said that the results are correct but if I were to run this test case where the loss function that have shown before it wouldn't work and why women work well in

08:10

the source code there is this global variable there and the loss function that we had before just import the the module at the end and if you know a bit about how important works in Python was involves the cash so that multiple test cases running the first one will actually import the module initialize the global variable all the other test cases will just we just get the cash import back and won't be neutralized again so the assumption of the test cases that the some almost always starts with neural but doesn't hold the the test cases would fail now there are several solutions to this time I'll just going to show you the the the simplest 1 and that looks like this the loss function still the same just the 1st line of the comment has changed for the better we generate a random name for the module so this model attention by importing essentially a new module every time this function is called which might not be the most performant solution and we also use more memory but it avoids nicely all the problems that you could otherwise have caching of data for this I use the the URI UID module which just generates a random unique ID and depends that to the file name which then used as the module all the other code and here is the same as before so each test case can still look the module and gets a fresh copy every time you could also implement that different way and when you when you import the module just re-initialized every time but that would make it would take more coats still identity

09:45

then example number 3 and you to modify now since all the other examples of basic was just a single seed financing filed now we take the 2nd header files and we want to do some mathematics complex numbers so we define our instructive that's not has just 2 fields for 2 part of a complex number for the real part and imaginary part and the other 1 had 5 and then we want to want to implement a function that uses this type so again we use the example of addition and into complex numbers and returning the results and we can implement it like this suggests that both parts together and return the result of the now the test case for this again doesn't really need to know much about the C code with local modulus before and you don't even have to deal with the complex types of declared somewhere when you want to call the eQTL function you just pass in the list here and see if I automatically generate structures for that so that the secret is happy and gets the correct results and also there result of this function call is a nice Python objects where you can access the parts of the structure with normal names and considered all this our results are correct it but again from this example to work because using the previous implementation of the loss functions because the previous implementation it just looked at the source file and the header file of the model that we want to test it doesn't really know about the other had father we also now if you remember the source that you could say yeah well the the other particle included into the model had a file so it should be present there but unfortunately see if I cannot deal with these include statements so what we need to do is we need to have some kind of preprocessing like this the predecessor of the source code so that there are no more include statements and there's no other directives to unify doesn't understand otherwise it would go on our and this is done with this preprocessed calling here again there are multiple ways we could implement that I've chosen to

12:10

just run the GCC predecessor the source code and get better results than the end of that 1 large string that contains content of both pedophiles and supervised the that now think the last example and is gets a a bit more complex because now we have some external dependencies this case you can imagine you're only 1 program might controller and maybe the vendor of the microcontroller provides you with nice library like this where they can reach you videos using simple function call and the lender has chosen to implement different functions for each you get you can access so he provides library that has this interface here but maybe you could give rather like to use this and you only want a single function call the permitted to select the GPO that you're interested in now you can implement that in your own code to just look at the parameter called the appropriate functions and you get parameter that you cannot deal with your return some kind of error code and this is the code that we want to come our unit test we don't want to test the vendors library so we don't want to use and that the EEG-BCI only 0 1 course here we probably use them in the unit test because they might exist some registers of microcontroller that aren't there in our test environments so we somehow need to replace those calls with our model function that we can run a test case that knows what that you care about is from the test is about looks like this on the 1st iteration converges to the previous implementation is not the loss function returned to values not only the modulus before but also that I object that's part of the of the flies interface and we use that in the 1st test case is to replace the Psi function that we don't want to use a Python implementation so we find a function that has the same name as the Psi function we want to replace and we tell the of fight I they when this function gets called please use this Python implementations that don't use the implementation that might come to find somewhere and so the Python implementation just can return a fixed value and the test case can call the function that we want to test the preparatory and see that the value of it that defined before internally and and the 2nd test case for the you number 1 that does the same thing but using a different constructs so in this case we don't want to really define function but we want to use the model Mama objects like you might be used to from the unit test library and you can't you just the same with the continuum of objects to return a value but it's called and then tells you that I had this function just something else that you can call use that in place of the C function and then the test is a gamble can call this function and at the end you can also use the same method that are provided by the metric more function and in this case again we need to modify the law functionality again for comparison the old implementation and we need to add some more code to that for this example the word on of the changes here all again marked with a comment the 1st change is that's not sufficient anymore to just process the header file for the module but actually need to process all the header files that are included in the module so that just use a regular expression to collect all the include statements then run that through preprocessing and as a result gets 1 last thing again that contains all the into statement of the of the content of the into 2 parts of all modules and the main verb lemmas down in the next 2 lines are wary need to tell the of high which functions we want to replace with Python code in which functions are implemented in our secret so the 1st line just goes through the source code and looks for all the function definitions so we know which functions are implemented by our source code and the 2nd line that goes through all the include many have looks for the function declarations in there and whenever it finds a function that is not implemented in the source code it will tell the if if I had please insert a Python implementation in here that we can replace later on the functionalities all variants we just need to fixed the function declarations with this extern Python plus the statement meant survival OK any to generate some code for that and this will already make the compiler happy find a reference for this function so it can call it and we can later replaced with Python code and in the end the last changes as a set 4 that we now need to return the set of i object from the log function so that the test cases can tell the idea about the the implementation of want now I'll actually in the what detail call this step in the middle words were analyze the source code to find a function definition In this is based on

17:34

seas positive and this is the 1st part that collects all the function definitions so spicy parser will analyze the source codes and will build an abstract abstract syntax tree out of it so you can later what the streets with the class that's already provided and whenever you hit a function definition this wizard function here is called people get the nodes of the tree and can just ask this OK what is the name of the function will add this to list and so in the end want it has walked through the poetry you get a list of all the the functions that are implemented in the source code for for all the names of the functions and this is then used in the 2nd part again based on on the policies of the modules are and when we actually pass all the include contents into an abstract syntax tree and then tell pricy father to regenerate the corresponding C code from that so that we can modify some bits of that and placing parser already has supports to regenerate code from the tree and we just took into that and whenever we see a declaration for function this is then again the visit function for declarations and we look at the big generation there and see whether function declaration and it is and the name for this declaration is not in the list of functions that found in the source code then adjust prefixes with the and present policy statement so that once you've identified as the source code it will know what to do with this function and think this was the last example that I want to show you so to some of the

19:14

time I want to talk quickly about some of the drawbacks that this

19:18

approach might have if you're used to other approaches and 1 of the main drawbacks is probably that if you use this code is optional you you see something bad and tries to access the Nov . 2 for example than to crash the test process because code actually runs in the same process there no boundaries between so when you see code destroy something you test progression from getting nicer reports and you might not like that so 1 solution to that problem would be to run each test case in a separate process and have 1 main process collect all the results man of 1 the test crashes just pressure the single test case is the main process can still report arrows and all the other test cases will continue to run this might add a little ahead of course because now you have multiple processes running in that year and he some more computing time but at the same point at the same time you can also run your tests in parallel so that model because it might actually faster in the and and running everything serial another big problem might be that debugging of test cases gets harder now because you've got a Python process that calls from C functions but again might call some pattern functions and wearing device that you can attach the back to your Python test cases but that won't help you much once the land you want the work of the c code does there we can attach a sea level the blogger so you can see what it tests of 40 implementation dust but then you have to deal with all the calls that that are done by the Python interpreter and if you need a script somehow so it would be nice for us to have some may be better integrated solutions here's some combination of 2 debuggers from 1 for the past 5 1 for the seaside but mostly hand over control once you enter the the other part on 1 could also argue that since we are talking about the unit tests years if you really need to be about unit tests maybe could also think about simplifying your code simplifying the unit test or even the implementation so that you don't need to divide them in order to find a problem but so that you've got to unit test that really can't tell you where the problem is when something breaks on button on positive note if you're going to remember something from this talk at selected at like you to remember that writing the test cases is really simple and no matter how complex statistical code looks like the all examples that I've shown you the test cases with pretty much the same because all the complexity that you need to care about is hidden inside the I the representative that showing you here the test case often you don't really need to deal with that you just can't concentrate inviting test cases and you need to solve the hot spots ones that imaginary part of the coat and never look at that again as long as word

22:14

so thank you for your attention been Thank you you is it that any questions and you can you running text from sea bed from Python also compiled a library for example thanks so much build a binary codes in you important that in the fight you know that is 1 of the main use case actually receive a finite you can interface from Python to existing libraries so that you can build a nice Python interface for for libraries that already exist without having to reinvent the so that of course possible this approach was moment to test the source of the past the source code of library but of course you can also tell it here use existing library and you could probably up also that do this they could you do this trick with markings like there uh puts their uh alternatives functioning on mean function definitions in unloaded so something like that do you think that this is possible as well the dependence of the function that you want to lock is not part of the library but would be part of another library and you don't ligand that's library manager of possible because then you have to insert your own implementation of a function anyway for it to comply but if you want to modify and that's part of the library that you want to test is implemented in there can't really replace because it's part of the same binary and the court will just call the function in there can't really take it out and incident as implementation there and so you can switch of or but binary code but the I would refactoring code say change the name of the friend of function of the signature and forgot to add up my test how easy it is to you know to support the mismatched related to probe error messages doesn't crash most of I will tell you if you want to call a function that doesn't exist that well there's no no such attitude on the module you get the usual error codes for that if you change the title for a dependent on how compatible the all type to the new type of you may be change into a float or something like that you might even you don't need to do to adapt to test cases in the past and the survivor just convert that to to float value and for your call and if I would like to use a different struck name also would to take the there will be original student to change the names so that are not compatible you you get an error message if you have on the other hand the structure that's completely different completely different but has the same of the same types in there and you probably won't notice the change the complex numbers structure for example interest switch the order of the fields you will notice that when you pass in the parametres you will notice that then when you test for the for the section number of questions this can these justify more you can be also used for musical postcode for what they see plus plus 4 C + + for our think it's not of Computer Supported no but that's the name the the vital permanent and for the 1st row because you you can ask him about new features the have short answer is no OK thank you for your attention and thank you right from the beginning with

Inhaltliche Metadaten

Alexander Steffen - Writing unit tests for C code in Python There are many unit testing frameworks for C out there, but most of them require you to write your tests in C (or C++). While there might be good reasons to keep your implementation in C (for example execution speed or resource consumption), those hardly apply to the tests. So wouldn't it be nice to use all the power of Python and its unit testing capabilities also for your C code? This talk will show you how to combine CFFI and pycparser to easily create Python unit tests for C code, without a single line of C anywhere in the test cases. It will also cover creating mock functions in Python, that can be used by the C code under test to hide external dependencies. Finally, we will look at some of the challenges you might face when trying to mix Python and C and what to do about them.