lunes, 20 de septiembre de 2010

Probando tu codigo en C++ con gtest

La necesitadad de probar las cosas
En las ultimas semanas me ha entrado un ataque de creatividad y he escrito bastante código para mi motor gráfico en eterna creación. La verdad es que los casos directos funcionan pero van apareciendo errores y eso te paraliza bastante porque tienes que pararte y analizar lo que has hecho y alguna que otra vez rediseñar y cambiar un monton de cosas.
Para evitar esto, ultimamente he estado buscando algunas herramientas de test unitarios para C++ y he encontrado una que me ha gustado bastante. Multiplataforma, simple y rapida, sin muchas florituras pero parece que bastante madura. Y con el sello de google. Sí, es google test. En este articulo voy a describir como preparar tu entorno Visual Studio con google test para empezar a probar tu código.
Compilando gtest desde las fuentes
Lo primero que vamos a hacer es bajar una copia del código fuente. Podemos obtener el código aqui. Yo bajo la 1.5.0. Ahora solo hay que descomprimir en un directorio de windows. Una vez descomprimido podemos ver que hay un subdirectorio llamado msvc. Aqui se guardan los proyectos para compilar las librerias de gtest. Veras que hay dos soluciones de proyecto (lo que antes eran los workspace de VC++): gtest.sln y gtest_md.sln. La diferencia entre uno y otro la explicaré más adelante. Nos quedaremos con el primero. Si tenemos Visual Studio 2005 la solución se abrirá directamente. Si tenemos Visual Studio 2008/2010 entonces se abrirá el convertidor de soluciones.

Una vez abierto, veremos que tenemos 4 proyectos incluidos en la solución.

No nos preocuparemos demasiado por que solo queremos generar las librerias. Hacemos click derecho en la solución y seleccionamos generar solución.
En principio, y si no pasa nada raro, deberia acabar sin errores ni advertencias, habiendo compilado los 4 proyectos. Si te fijas, dentro del directorio msvc, ahora existe una carpeta llamada gtest. Dentro de gtest, se ha creado otra llamada Debug. Y dentro, se han creado las librerias y ejecutables resultantes de la compilación. Veras que tienes un archivo llamado gtest_maind.lib y otro llamado gtestd.lib. Estos son las librerias estaticas que necesitamos para nuestros tests.

Ahora vamos a compilarlas en modo Release. Vamos a Visual Studio y arriba, en la barra de herramientas estandar, cambiamos la configuración de Debug a Release. Ahora, como antes, hacemos click derecho en la solución y generamos la solución. Como antes vamos a msvc->gtest, pero ahora veremos dos directorios. Uno de ellos se llama Release. Tambien contiene dos ficheros .lib.

Instalar gtest en nuestra máquina

Dentro del directorio de gtest, existe un directorio llamado include. Ademas, ya hemos explicado donde quedan los .lib tanto de degub como de release. Ahora podemos hacer dos cosas. O le explicamos al IDE donde encontrar estos dos directorios o le decimos a cada proyecto donde estan. En general soy partidario de lo segundo, pero en este caso lo voy a instalar de manera global puesto que esta es una libreria de uso muy general y usable en casi cualquier proyecto, como boost, por ejemplo.

Asi que en Visual Studio voy a Herramientas, opciones... y alli voy a proyectos y soluciones y selecciono Directorios VC++. Alli añado el directorio Debug y Release resultante de la compilación anterior en el apartado de archivos de biblioteca, y añado el directorio include dentro de archivos de inclusion:
Le doy a aceptar y ya tenemos "instalado" gtest en nuestro IDE

El primer proyecto de tests de unidad

Lo unico que necesitamos hacer para crear un test minimo de gtest es lo siguiente. Creamos un nuevo proyecto, en general dentro de la solución donde estemos creando nuestro proyecto principal y le ponemos un nombre que parezca importante (por nuestro ego y todo eso...), como unit_Tests. El proyecto, lo haremos vacio y de win32 console.

Ahora hacemos click derecho en el proyecto y vamos vinculador --> entrada -->dependencias adicionales y alli añadimos, gtestd.lib y gtest_maind.lib si estamos en Debug, y lo mismo pero sin la 'd' si estamos en release.

Nota!!
Si no hubieramos configurado nuestro IDE para saber donde estan estos archivos necesitariamos decirselo añadiendo los directorios de los .lib en vinculador-->general-->directorios de bibliotecas adicionales.

Ademas deberiamos instruir al proyecto sobre donde se encuentran los directorios de include, poniendoles su situacion en C/C++ --> directorios de inclusion adicionales.

Y ahora ya tenemos preparado nuestro proyecto para ejecutar test. Vamos a crear un archivo unit_tests.cpp (por ejemplo), con el siguiente código:


#include <gtest/gtest.h>

TEST(UnitTestSample,TestOne){
EXPECT_EQ(1,1);
}

Te recomiendo que ejecutes con CTRL-F5 para que la ejecución se pare antes de cerrar la ventana. Deberias ver esto:
Y eso es todo!! Con esto ya puedes probar de manera automatica que 1 es igual a 1, y muchas otras cosas más. Ahora importa tu código y demuestra que es robusto como una roca!!!

A continuación algunas explicaciones que he dejado para el final para no desviarme del tema principal.

Nota1: Te habrás dado cuenta de que el codigo no tiene función main. Esto es por que al añadir gtest_main(d).lib, ya añadimos un main que ejecuta todos nuestros test. Puedes consultar en la documentación del proyecto como generar tu propio main. Pero te recomiendo que lo uses asi, puesto que el código más seguro es el que no se escribe.


Nota2: Antes he explicado que hay que elegir la solución gtest y no la gtest_md. Bueno, en realidad lo importante es que si eliges gtest_md, entonces en la configuración de tu proyecto debes elegir en C/C++ --> generacion de código --> biblioteca en tiempo de ejecucion la opcion depuracion multiproceso si es en Debug y mutiproceso si estamos en Release. Si por el contrario elegimos gtest, entonces seleccionaremos DLL multiproceso.

miércoles, 1 de septiembre de 2010

Comprobar "Endianess"

Pequeño truquito para comprobar como se ordenan los bytes en la memoria de la maquina:

int i = 1;
char c = *(char *) &i;

if (c) {
cout << "Little endian" << endl;
} else {
cout << "Big endian" << endl;
}


Encontrado via:

De std::wstring a std::string

Una manera practica para pasar de std::wstring a std::string y viceversa:


#include <string>
#include <algorithm>

// Prototype for conversion functions
std::wstring StringToWString(const std::string& s);
std::string WStringToString(const std::wstring& s);

std::wstring StringToWString(const std::string& s)
{
std::wstring temp(s.length(),L' ');
std::copy(s.begin(), s.end(), temp.begin());
return temp;
}


std::string WStringToString(const std::wstring& s)
{
std::string temp(s.length(), ' ');
std::copy(s.begin(), s.end(), temp.begin());
return temp;
}


Encontrado via: http://www.codeguru.com/forum/showthread.php?t=193852