TeraBIThia

26/06/08

Test de carga con WatiN

Archivado en: Uncategorized — crisfervil @ 4:41 pm

Pego este snippet por si a alguno le sirve. Lo he escrito a raiz de la necesidad de realizar una prueba de carga sobre una página aspx de una manera rápida y gratis.

Por si alguno no conoce qué es Watin, dejo un enlace: http://watin.sourceforge.net/

Watin es un framework gratuito que permite automatizar el navegador. Se basa en Watir que es otro framework que hace lo mismo pero en Ruby. Inicialmente sólo soportaba la automatización de Internet Explorer, pero actualmente funciona también con Firefox.

Un getting started para watin: http://watin.sourceforge.net/gettingstarted.html

Pues bien, resulta que la mayor complicación a la hora de usar Wating para realizar una prueba de carga, reside en que Watin necesita que todos los hilos que inician una ventana del navegador se ejecuten en un “Apartment” independiente.

Un Apartment en el mundo Windows es un contenedor lógico dentro de la memoria que impide que se compartan datos entre hilos. Es un mecanismo de seguridad que evita que se produzcan Memory Leaks (pérdida de datos) y bloqueos.

El Framework de .net no usa Apartments y todos los objetos manejados son responsables de gestionar los mecanismos de proporciona la plataforma para evitar los problemas que resuelve el uso de Apartments.

Debido a que las COM si usa Apartments el CLR necesita porder crear e inicializar un Apartment cuando trabaja en un entorno de “Interoperabilidad“.

La gestión de los Apartments en .net la proporciona la propiedad ApartmentState de la clase System.Threading.Thread (http://msdn.microsoft.com/en-us/library/system.thr…)

Manos a la obra. Primero, creraremos el código que ejecutará la petición, mediante Watin de la página en cuestión.

/// Este es el método que llamaremos en cada hilo que ejecute la pruebas de carga.
/// Debe cumplir la firma del delegado System.Threading.ParameterizedThreadStart
public void LoadTest(object param)
{
    string sParam = (string)param;
    IE ie = new IE("http://www.google.es");
    ie.TextField(Find.ByName("q")).TypeText(sParam);
    ie.Button(Find.ByName("btnG")).Click();
    ie.Close();
}

Este código abre una ventana del navegador, navega hasta la página de google, realiza una búsqueda y se cierra.

Luego, nos falta un bucle para lanzar n hilos que ejecuten esta petición.

for (int i = 0; i < 15; i++)
{
    // Delegado al método a ejecutar
    ParameterizedThreadStart ts = new ParameterizedThreadStart(LoadTest);
    // instancia del hilo
    Thread newThread = new Thread(ts);
    // Muy importante para que Watin se ejecute correctamente:
    // Indicamos aquí que el hilo debe ejecutarse eun un "Apartment" independiente
    newThread.SetApartmentState(ApartmentState.STA);
    // lanzar el hilo (le pasamos un parámetro cualquiera)
    newThread.Start(@"1+" + i.ToString());
}

Y ya está! si ejecutamos este código, veremos abrir el navegador 15 veces y ejecutar la petición.

Lo refinamos un poquito. Podemos por ejemplo, añadir código para especificar un timeout, y que el hilo principal espere a que todos los demás acaben.

Lo primero que necesitaría, es guardar la referencia a todos los hilos que voy creando en el bucle. Me creo por tanto una lista de hilos lanzados.

// Lista de hilos lanzados
List<Thread> launchedThreads = new List<Thread>(); 

Y con esto, me creo un méodo de espera, que no hará otra cosa que esperar a que todos los hilos lanzados acaben.

/// Espera a el tiempo especificado a que los hilos de la lista
/// hayan acabado de ejecutarse
private void WaitForComplete(List<Thread> launchedThreads, int timeOut)
{
    // La hora a la que he comenzado a esperar
    DateTime startTime = DateTime.Now;
    // Tiempo transcurrido desde que empezado a esperar hasta la hora que
    // evalúo el timeout
    TimeSpan elapsedTime = DateTime.Now.Subtract(startTime);
    // Me indica si debo seguir esperando
    bool keepWating;
    do // bucle de espera
    {
        keepWating = false;
        foreach (Thread t in launchedThreads)
        {
            // Si hay al menos un hilo en ejecución seguimos esperando
            if (t.ThreadState != ThreadState.Stopped)
            {
                Thread.Sleep(100);
                elapsedTime = DateTime.Now.Subtract(startTime);
                keepWating = true;
                break;
            }
        }
    }
    while (keepWating && elapsedTime.TotalMilliseconds < timeOut);
}

Entonces, lo que deberé hacer será, llamar a este método a continuación del bucle.

// Esperamos a que acaben de ejecutar todas las pruebas.
// El timeout se especifica en milisegundos.
WaitForComplete(launchedThreads, 60000);

Y ya está. Es bastante impresionante ver lanzar 50 ventanas del navegador de una vez. Eso sí, hace falta un porrón de memoria para que la cosa vaya medianamente bien (no es mi caso).

Y… un último detalle. Lo añadimos todo a una proyecto, añadimos referencia al NUnit y ya lo tenemos listo para ejecutar desde una consola de pruebas.

Código para descargar. http://cid-da2e56199efa5596.skydrive.live.com/self.aspx/BlogAttachments/WatinLoadTest.zip

Crossposted from crisfervil.com

25/06/08

Agregando Tareas a la ThreadPool

Archivado en: General — crisfervil @ 1:05 pm

La clase ThreadPool permite lanzar tareas asíncronas evitándonos la engorrosa gestión de prioridades, carga de procesador, seguridad, etc.

Apunto este snippet por aquí que luego siempre se me olvida cómo se hace.

class Program
{
    static void Main(string[] args)
    {
        Runner r = new Runner();
        r.Run();
        Console.WriteLine("Todas las llamadas finalizadas! (Las tareas de la pool siguen corriendo)");
        Console.ReadLine();
    }
}
class Runner
{
    public void Run()
    {
        for (int i = 0; i < 10; i++)
        {
            // Agregamos tareas a la ThreadPool
            // Como parámetros pasamos referencia a un método que cumpla
            // la firma del delgado System.Threading.WaitCallBack y un parámetro
            System.Threading.ThreadPool.QueueUserWorkItem(new WaitCallback(Task), i);
        }
    }
    // Método que será ejecutado por la ThreadPool.
    // Recibiremos como parámetro lo que hayamos pasado al agregar la tarea a la pool
    private void Task(object number)
    {
        int taskNumber = (int)number;
        Console.WriteLine("Iniciando tarea {0}...",taskNumber.ToString());
        // Esperamos un tiempo aleatorio entre 1 y 10 segs.
        Thread.Sleep(new Random((int)DateTime.Now.Ticks).Next(1000,10000));
        Console.WriteLine("Finalizada tarea {0}!", taskNumber.ToString());
    }
}

Esta sería la salida que produce este snippet:

Para tareas más sencillas podemos usar delegados anónimos, en lugar de tener que  crear un método para que sea ejecutado por la pool.

public void RunAnonymous()
{
    for (int i = 0; i < 10; i++)
    {
        // Agregamos tareas a la ThreadPool
        // Para la llamada usamos un delegado anónimo que debe cumplir la
        // firma de System.Treading.WaitCallBack
        string nombreEmpleado = "Empleado " + i.ToString(), idEmpleado = i.ToString();
        ThreadPool.QueueUserWorkItem(delegate(Object param) { ProcesarEmpleado (idEmpleado, nombreEmpleado); });
    }
}
// Procesa los datos del empleado, etc, etc, etc
private void ProcesarEmpleado(string idEmpleado, string nombreEmpleado)
{
    Console.WriteLine("Procesando {0}/{1}...", idEmpleado, nombreEmpleado);
    // Esperamos un tiempo aleatorio entre 1 y 10 segs.
    Thread.Sleep(new Random((int)DateTime.Now.Ticks).Next(1000, 10000));
    Console.WriteLine("Fin proceso empleado {0}/{1}!", idEmpleado, nombreEmpleado);
}

Cómo podríamos saber cuándo han acabado de ejecutarse todos los hilos?

Creamos un flag a nivel de módulo en la clase Runner, que nos indicará cuántos hilos en ejecución hay actualmente.

private int _runningTasksCount = 0;

Hay que tener mucho cuidado con las variables accedidas por varios hilos a la vez, pues es posible que cada procesador (físico o lógico) tenga una copia del valor de esta variable. Si dos hilos acceden simultáneamente, y lo que es peor, la actualizan simultáneamente, puede producirse una pérdida de datos.

// Método que será ejecutado por la ThreadPool.
// Recibiremos como parámetro lo que hayamos pasado al agregar la tarea a la pool
private void Task(object param)
{
    // Indicar que hay una tarea más ejecutándose en la pool
    Interlocked.Increment(ref _runningTasksCount);
    int taskNumber = (int)param;
    Console.WriteLine("Iniciando tarea {0}...", taskNumber.ToString());
    // Esperamos un tiempo aleatorio entre 1 y 10 segs.
    Thread.Sleep(new Random((int)DateTime.Now.Ticks).Next(1000, 10000));
    Console.WriteLine("Finalizada tarea {0}!", taskNumber.ToString());
    // Indicar que hay una tarea menos ejecutándose en la pool
    Interlocked.Decrement(ref _runningTasksCount);
    // Hemos acabado
    if (Thread.VolatileRead(ref _runningTasksCount)<1)
    {
        Console.WriteLine("Todas las tareas completadas!");
    }
}

La clase System.Threading.Interlocked posee mecanismos para modificar datos de forma segura. En el ejemplo lo que hacemos es incrementar el valor del flag usando el método Increment, y lo decrementamos usando Decrement.
Finalmente para una lectura segura (puede ocurrir que la copia de la variable que tiene el procesador en el que se ejecuta el hilo no tenga un valor actualizado) usamos el método estático VolatileRead de System.Threading.Thread

Ocurre que como el hilo principal tiene como única tarea lanzar a los demás hilos, acaba antes que los demás. Si nuestra aplicación es como la del ejemplo, y el hilo principal se ejecuta en una aplicación de consola, la aplicación finalizará antes que los hilos acaben de ejecutarse. Es posible que en este caso, al finalizar el proceso, los demás finalizen también “a capón”.

Por esto necesitamos un mecanismo que impida que la consola se cierre antes que todos los hilos acaben su trabajo.

Yo lo gestionaría envolviendo el flag que acabamos de crear en una propiedad de sólo lectura y la consultaría desde el main de la consola.

public int RunningTasksCount
{
    get { return Thread.VolatileRead(ref _runningTasksCount); }
}

static void Main(string[] args)
{
    Runner r = new Runner();
    r.Run();
    Console.WriteLine("Carga de la pool OK! (Las tareas de la pool siguen corriendo)");
    // Esperar a que todo haya acabado
    while (r.RunningTasksCount > 0)
    {
        Thread.Sleep(100);
    }
    Console.ReadLine();
}

Código Fuente

Crossposted from crisfervil.com

24/06/08

Varias Versiones de Internet Explorer en la misma maquina

Archivado en: Uncategorized — crisfervil @ 7:01 am

Apunto estos enlaces que me han parecido muy interesantes. Se trata de la posibilidad de usar varias versiones de Internet Explorer en el mismo equipo. Hasta ahora pensaba que esto sólo era posible mediante máquinas virtuales.

La pega es que no corre sobre Windows Vista.

http://tredosoft.com/Multiple_IE

Existe también esta otra herramienta,  aún en fase beta, pero que corre sobre  Vista: http://www.my-debugbar.com/wiki/IETester/HomePage

[Actualización]

Finalmente esta página, que procesa la url (debe ser pública) que le indiquemos hasta en 67 combinaciones distintas de navegador y OS y con diversas configuraciones (Tamaño Pantalla, Profundidad de color, Javascript, Java, Flash, etc.) y nos enseña la captura de pantalla: http://browsershots.org/

Cómo se vería http://cs.crisfervil.com en Dillo 0.8.6 PLD Titanium y en K-Meleon 1.1.4 / Windows 2000

 

Crossposted from crisfervil.com

04/06/08

Usando Text Templates desde la linea de comandos

Archivado en: General — crisfervil @ 12:12 pm

Es posible usar las plantillas de texto de visual studio (t4) directamente desde la línea de comandos.

Para ello, hay que ejecutar el programa TextTransform.exe que encontraremos en la carpeta
%Program Files%\Common Files\Microsoft Shared\TextTemplating\1.1

[sustituir %Program Files% por la ruta de la carpeta Archivos de Programa]

Hay que pasar como parámetro el archivo de .tt que queremos transformar.

Si lo ejecutamos sin parámetros tenemos la definición de los parámetros que acepta el programa

C:\Program Files\Common Files\Microsoft Shared\TextTemplating\1.1\TextTransform.exe

TextTransform - Transforms the given text template.

  This command runs a transform using a text template.

 Usage: TextTransform [<options>] <templateName>

 Options:

  -out <filename>: The file to write the output of the transform to.

  -r <assembly>: Reference an assembly for compiling and running the text
    template

  -u <namespace>: Import a namespace for use within the text template

  -I <includedirectory>: Provide a directory containing text templates included
    from within the given text template

  -P <referencepath>: Provide a directory to search for referenced assemblies
    specified within the text template or using the -r option

  -dp <directiveprocessorname!directiveprocessortype![assembly|codebase]>:
    Specify the name, full type name, and assembly of a directive processor
    that can be used to process custom directives within the text template

  -a <[directiveprocessor]![directiveid]!parametername!parametervalue>: Specify
    a parameter that a directive processor can query for as a name/value pair.
    The directive processor and identifier are optional allowing parameters to
    be specified for any directive processor or any instance of a particular
    directive processor.

Fuente:

http://blogs.msdn.com/garethj/archive/2007/08/14/transforming-dsl-setup-t4-templates-from-the-command-line.aspx

http://msdn.microsoft.com/en-us/library/bb126245.aspx

Crossposted from crisfervil.com

Blog de WordPress.com.