Cpputest para test unitario de drivers en c embebido y buenas prácticas de arquitectura de sw embed

106 views
Skip to first unread message

Geni Suarez

unread,
Dec 15, 2016, 7:57:41 AM12/15/16
to Embebidos32

Buenas, soy nueva en el grupo y un poco junior en sistemas embebidos también. Me empiezo a dedicar en serio y trabajando con herramientas profesionales que desconocía o todavía no domino. 
He visto que por aquí alguien habló de este framework para test unitario de código y que puede haber quienes tengan más conocimiento o conceptos más claros que yo al respecto. 
No sé si estoy en el lugar del foro adecuado, si existe un tema para presentarse a la comunidad etc, así que ruego me indiquen si estoy haciendo algo mal. 

No llevo mucho haciendo tests y día a día aprendo algo más que el anterior. Ahora me encuentro ante un conflicto entre el test de un driver y su driver cuando ejecuto el test. Sé cuál es la fuente de conflicto y por qué sucede (un parámetro que no es el esperado). Todos los libros y ejemplos consultados no especifican si aquello que trato de hacer en el mundo de los tests se puede hacer o no. Tengo la ligera sospecha que no hay alternativa y por tanto debe subsanarse modificando la arquitectura de la fuente. Básicamente mi intuición es esta por la información que he podido consultar. Por la omisión de casos como el que pretendo resolver, interpreto que quizás esté "prohibido". Pero quiero asegurarme primero que realmente no haya herramientas en cpputest para hacerlo. Ya que a priori no me parecería tan extraño que en un código fuente modifique una variable de entrada en una función, variable tratada, que más tarde (antes de salir de dicha función) pasará como argumento de una llamada a otra función de nivel inferior (llamada a una función dentro de una función). O tal vez no sea tan "normal" a la hora de hacer una buena y correcta arquitectura. 

Me encuentro que no puedo pasar la dirección de un vector (o digamos variable X) desde el código fuente a su código test (para usarla de parámetro de entrada en una función mockeada). Existe alguna forma de compartir el valor de una dirección de una variable modificada en un driver.c y llevarla como parametro a una función mockeada dentro de su test, ej: 

Driver.c:
..
void init(*structinit)
{ uint8_t X = 0;
..
..

 X = a + b; //supongamos q a y b son otras operaciones tratadas con mascaras y algunas mas complejas operaciones

...
...
}
..
TEST Drivertest.c:
{
...
(mock().expectonecall("funcion").withParameter("pcbuffer",&X);
...
}

Digamos que para compilar me veo obligada a declarar X también en el test. Aquí es donde empieza la ambigüedad. Por que tengo dos posiciones de memoria diferentes para referirme al mismo parámetro. Es cuando se queja el test. Porque las direcciones de X en la función mock y la de la llamada equivalente del driver.c jamás coincidirán, se han creado dos ubicaciones en lugar de una (está claro). 

Si alguien es tan amable de darme su punto de vista, opinión tanto en la arquitectura que planteo como en uso de tests o rectificarme estaré encantada y muy agradecida. 

martin ribelotta

unread,
Dec 15, 2016, 8:38:36 AM12/15/16
to embebidos32@
Segun entiendo, eso que queres hacer es test de caja gris, y no siempre es recomendable.
La mayoria de los test unitarios estan pensados para manejar test de caja negra, los cuales solo tienen en cuenta parametros de entrada y salida.

La unica forma que veo de hacer eso, es cambiar la interfaz de la función para que, ademas, retorne la dirección de la variable.
Eso es bastante contraproducente si hablamos de variables en el stack porque al retornar de la función bajo prueba, el compilador no garantiza su integridad (puede ser pisada por sucesivas llamadas de otras funciones, muchas veces hechas encubierto al crear/destruir objetos temporales, todo dependiente de tu codigo)

Si la variable no esta en el stack sino en el heap, yo haria un mock de la función new/malloc (o la que genere ese recurso) y tomaria de ahí el valor requerido del puntero generado:


Si la unica forma de acceder a la variable es en el stack, yo replantearia el test de forma de hacerlo blackbox en vez de greybox.

No se si estoy entendiendo bien el problema, pero si no concuertda con lo necesario, podria ser de ayuda contarnos datos mas concretos.

Saludo.

--
-- Recibiste este mensaje porque estás suscripto al Grupo Google Embebidos32. Para postear en este grupo, escribe un email a embeb...@googlegroups.com. Para des-suscribirte, envía un email a embebidos32+unsubscribe@googlegroups.com. Para más opciones, visita el sitio del grupo en https://groups.google.com/d/forum/embebidos32?hl=es
---
Has recibido este mensaje porque estás suscrito al grupo "Embebidos32" de Grupos de Google.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a embebidos32+unsubscribe@googlegroups.com.
Para acceder a más opciones, visita https://groups.google.com/d/optout.

Geni Suarez

unread,
Dec 16, 2016, 3:59:15 AM12/16/16
to Embebidos32
De acuerdo, muchas gracias por tomarte la molestia de responder de forma rápida y atender mis dudas. Voy a tratar de dar más detalles y espero sean útiles.

Driver.c:
void init(*structinit)
{
  uint8_t X = 0;


  // ...


  X = a + b; //supongamos q a y b son otras operaciones tratadas con mascaras y algunas mas complejas operaciones


  funcion(&X);

  // ...

}
TEST Drivertest.c:
{
  // ...

  mock().expectonecall("funcion").withParameter("pcbuffer",&X);

  // ...
}

funcion(unit8* pcbyte) imagina que es una función de más bajo nivel tipo lectura o escritura que implica hardware y que el trozo de código de test fuera para testar la función init(*struct) del driver. 

Si te fijas tal como lo tengo indicado en el post anterior, donde test driver.c, para que me compile y lo pueda ejecutar, me obliga a declarar la variable X en test. Eso es lo que obviamente ocasiona dos direcciones diferentes para el mismo parámetro (no puedo hacerlo global porque no tengo permiso para tocar el código fuente como tester). El código driver.c que me han pasado está implementado de forma que se le pasa el valor de la dirección y no el dato. Claro, así jamás van a coincidir y el test me lanzará un mensaje como "parameter not expected" en llamada a la función en driver.c. Entender entiendo lo que está sucediendo y por qué no coinciden ni coincidirán nunca haciéndolo así. 

No soy una experta ni domino todavía la teoría de los unit test o las opciones que permite el framework cpputest. Pensé que al igual que es posible pasar parámetros desde test hacia el driver gracias a los mocks, a la inversa podría estar permitido a través de alguna instrucción o forma que aún no he sabido reconocer en el material, libros, blogs, etc.. consultadas. Sospecho que no sólo debe ser desaconsejable sino inexistente dicha opción. 

Por otra parte, estoy pasando un test a un código fuente de un driver de un tercero que me toca probar y como sabrás el tester no debe tocar ni modificar el código. Sin embargo, elaborando un test para los códigos de otros es posible detectar prácticas conflictivas o errores de arquitectura de los códigos fuente. Así que siendo fiel yo puedo advertir e informar si existe problemática para que el autor del código lo modifique. Si se prestan a modificarlo, bien. El problema es si los autores o proveedores del código se me niegan a cambiar su estructura y me insisten en que la variable X se debe actualizar en el driver después de inicializarse y ser pasado como parámetro a la función. De forma que entonces yo no puedo imponer desde test esa condición de entrada (valores reales) para el parámetro de las llamadas mockeadas -pues su código driver lo modifica-. Lo cual entonces, rompe con el concepto de auténtica caja negra. Si digamos tuviera la fuerza de que oficialmente no existe forma de pasarle un parámetro modificado desde el driver hacia el test -para pasarlo al mock expectonecall- sí podría yo forzarles a cambiar su código fuente porque he detectado un error. 

Tiene que ser un puntero porque la función escribe o lee registros y es dirección de buffer -aunque tal como lo puse en el ejemplo para simplificar sería una dirección de byte-. Pero lo que les propondría yo es la forma de actualizar esa variable. Por ejemplo que creen una función para actualizar esa batería de registros y lo hagan desde otro nivel. Si es que eso mejora las prácticas de una arquitectura adecuada de sw y deja de violar  las reglas del testing (desconozco y trato de averiguar si esto que comento es una violación de norma para un buen testing). 


Saludos y de nuevo muy agradecida. 




Christian N

unread,
Dec 16, 2016, 10:27:12 AM12/16/16
to Embebidos32
Hola

De lo que se observa y lo que pude entender de la explicacion, el test que se deberia realizar es pasarle el vector de prueba a los parametros que conforman a la variable X y analizar si la funcion Init cumple con la especificacion.

Intentar validar la variable X no lo veo dentro del ambito de los test

El detalle no es suficiente para poder evaluar si la aproximacion es correcta. No obstante de lo que se desprende de lo enviado, retirero que lo que se debe analizar y validar es que init satisfaga la mision para la cual fue concebida para realizar una autentico black box, pero sabiendo cuales son los parametros que toma (no X sino lo que hace que X sea tal o cual valor).

Si se quiere algo mas caja gris, puede que "funcion" sea global, y estando en kernel a menos que te la declaren static, podrias tener visibilidad y llamarle desde el tester. Pero ahi filosoficamente estarias evaluando "funcion" en base a lo que pudiera contener X.

Espero haber sido claro en estas pocas lineas

Geni Suarez

unread,
Dec 16, 2016, 11:03:58 AM12/16/16
to Embebidos32
Como tú dices y el compañero antes, yo también creo que sale del concepto de test unitario. Al pasar una variable modificándola desde dentro del driver (código a testar) no tendría sentido en la filosofía de test modular. Si fuera un dato normal, se podría tomar todo el trozo de código que modifica la X y replicarlo en el test para llegar a obtener el mismo valor. Pero no le veo solución cuando no hablamos de valores de contenido, sino valores de dirección. Tendremos dos direcciones siempre. 

Por la forma que me explica el compañero de llevarlo a cabo para pasarle el valor desde driver a test, aún lográndolo el intercambio, no obtendría un test fiable con garantías de mantener datos incorruptibles, etc.. Creo que es suficiente explicación para justificar que debe modificar la estructura de código fuente si se quiere mantener una compatibilidad con unit test.
Esa es la conclusión a la que llego hoy. 

MC

unread,
Dec 16, 2016, 11:56:08 AM12/16/16
to Embebidos32

Hola Geni,

Leí tu pregunta y quería ayudarte pero bueno la verdad es que nunca sentí nombrar sobre los test unitarios.

Sino te molesta podrías explicarme en pocas palabras de que se trata o sino pasarme algún buen link para leer.

Podría buscarlo en google ciertamente, pero bueno si podés ayudame a aprender sobre eso estaría bueno.

Gracias y saludos

Mauricio

Guillermo Villamayor

unread,
Dec 16, 2016, 7:48:15 PM12/16/16
to embeb...@googlegroups.com
Mauricio los tests unitarios son tests que se hacen para probar una única cosa, generalmente una clase o un método pero puede ser una función. Sirven para probar los componentes individuales y no el diseño del software. El equivalente electrónico sería un probador de resistencias, capacitores, transistores etc.
--
-- Recibiste este mensaje porque estás suscripto al Grupo Google Embebidos32. Para postear en este grupo, escribe un email a embeb...@googlegroups.com. Para des-suscribirte, envía un email a embebidos32...@googlegroups.com. Para más opciones, visita el sitio del grupo en https://groups.google.com/d/forum/embebidos32?hl=es

---
Has recibido este mensaje porque estás suscrito al grupo "Embebidos32" de Grupos de Google.
Para cancelar la suscripción a este grupo y dejar de recibir sus mensajes, envía un correo electrónico a embebidos32...@googlegroups.com.

Geni Suarez

unread,
Dec 19, 2016, 7:37:06 AM12/19/16
to Embebidos32
Buenas, MC

Perdona el retardo en responder. Te paso un link (al final) sobre el concepto de test unitario en español para que te hagas una idea de qué se habla. Luego para ponerlo en práctica en un entorno de software embebido hay que basarse en otras herramientas que se te adapten mejor a ti, donde influye también si testeas con Linux o Windows. Por ejemplo estoy usando cpputest (una especie de pluggin open source http://cpputest.github.io/) que sólo corre en Unix (linux y mac); pero si quieres puedes usar una máquina virtual desde windows. 
La mayoría que conozco usan Cygwin y les funciona. No obstante empecé con esta y no me acabó de gustar. Al tener la opción de trabajar directamente sobre Linux me olvidé de las MV. Pero si no me quedara más remedio habría probado VMware. Este link está dirigido a la comunidad de microsoft, pero este capítulo es aplicable a un buen punto de introducción para saber "de qué trata" esta filosofía. Por eso no te fijes mucho en la sintaxis de ejemplos de código que puedan aparecer.


Un saludo y gracias por tu interés. 
Reply all
Reply to author
Forward
0 new messages