ventajas pruebas metodologia ejemplo automatizadas automatización c++ multithreading unit-testing testing

c++ - metodologia - pruebas automatizadas selenium



Cómo escribir una prueba automatizada para seguridad de hilos (6)

Tengo una clase que no es segura para subprocesos:

class Foo { /* Abstract base class, code which is not thread safe */ };

Además, si tiene objetos foo1 y foo2, no puede llamar a foo1-> someFunc () hasta que devuelva foo2-> anotherFunc () (esto puede suceder con dos subprocesos). Esta es la situación y no se puede cambiar (una subclase Foo es en realidad un contenedor para una secuencia de comandos python).

Para evitar llamadas no deseadas, he creado lo siguiente:

class FooWrapper { public: FooWrapper(Foo* foo, FooWrappersMutex* mutex); /* Wrapped functions from Foo */ };

Internamente, FooWrapper envuelve las llamadas a las funciones Foo con el mutex compartido.

Quiero probar FooWrapper para seguridad de hilos. Mi mayor problema es el hecho de que los hilos son administrados por el sistema operativo, lo que significa que tengo menos control sobre su ejecución. Lo que me gustaría probar es la siguiente situación:

  • El hilo 1 llama a fooWrapper1-> someFunc () y bloquea mientras está dentro de la función
  • El subproceso 2 llama a fooWrapper2-> anotherFunc () y lo devuelve inmediatamente (ya que someFunc () aún se está ejecutando)
  • El hilo 1 finaliza la ejecución

¿Cuál es el más simple para probar un escenario como este automáticamente?

Estoy usando QT en Win32, aunque preferiría una solución que sea al menos multiplataforma como lo es QT.


Cuando comienzas a hacer subprocesos múltiples, tu código se vuelve, por definición, no determinista, por lo que probar la seguridad de subprocesos es, en el caso general, imposible.

Pero para su pregunta muy específica, si inserta largos retrasos dentro de Foo para hacer que cada método Foo tarde un tiempo, entonces puede hacer lo que le pida. Es decir, la probabilidad de que el primer hilo vuelva antes de que el segundo hilo entre en la llamada se vuelve esencialmente cero.

Pero, ¿qué es lo que realmente estás tratando de lograr? ¿Qué se supone que esta prueba debe probar? Si intenta validar que la clase FooWrappersMutex funciona correctamente, esto no lo hará.


En lugar de simplemente verificar que un hilo en particular esté terminado o no, ¿por qué no crear un Foo falso para ser invocado por su envoltorio en el cual las funciones registran el tiempo en el que realmente se iniciaron / completaron? Entonces su hilo de rendimiento solo necesita esperar el tiempo suficiente para poder distinguir la diferencia entre los tiempos grabados. En su prueba, puede afirmar que la another_func de inicio de another_func es posterior a la hora de inicio de some_func y que el tiempo completado es anterior al tiempo de some_func completada. Dado que su clase falsa solo está registrando los tiempos, esto debería ser suficiente para garantizar que la clase contenedora esté funcionando correctamente.

EDITAR : Usted sabe, por supuesto, que lo que hace su objeto Foo podría ser un antipatrón , es decir, un acoplamiento secuencial . Dependiendo de lo que haga, puede manejarlo simplemente haciendo que el segundo método no haga nada si aún no se ha llamado al primer método. Usando el ejemplo del enlace de acoplamiento secuencial, esto sería similar a hacer que el automóvil no haga nada cuando se presiona el pedal del acelerador, si el automóvil aún no se ha encendido. Si no hacer nada no es apropiado, puede esperar y volver a intentar más tarde, iniciar la "secuencia de inicio" en el hilo actual o manejarlo como un error. Todas estas cosas podrían ser impuestas por su envoltorio también y probablemente serían más fáciles de probar.

También es posible que deba tener cuidado para asegurarse de que el mismo método no se invoque dos veces en secuencia si se requiere una llamada a otro método.


Es posible que desee comprobar CHESS: una herramienta de prueba sistemática para software concurrente de Microsoft Research. Es un marco de prueba para programas multiproceso (tanto .NET como código nativo).

Si lo entendí correctamente, reemplaza las bibliotecas de subprocesos del sistema operativo por las suyas, para que pueda controlar la conmutación de subprocesos. A continuación, analiza el programa para descubrir todas las formas posibles en que las secuencias de ejecución de los subprocesos pueden intercalarse y vuelve a ejecutar el conjunto de pruebas para cada intercalado posible.


Hasta ahora he escrito el siguiente código. Algunas veces funciona y algunas veces falla la prueba, ya que el modo Sleep no es suficiente para ejecutar todos los hilos.

//! Give some time to the other threads static void YieldThread() { #ifdef _WIN32 Sleep(10); #endif //_WIN32 } class FooWithMutex: public Foo { public: QMutex m_mutex; virtual void someFunc() { QMutexLocker(&m_mutex); } virtual void anotherFunc() { QMutexLocker(&m_mutex); } }; class ThreadThatCallsFooFunc1: public QThread { public: ThreadThatCallsFooFunc1( FooWrapper& fooWrapper ) : m_fooWrapper(fooWrapper) {} virtual void run() { m_fooWrapper.someFunc(); } private: FooWrapper& m_fooWrapper; }; class ThreadThatCallsFooFunc2: public QThread { public: ThreadThatCallsFooFunc2( FooWrapper& fooWrapper ) : m_fooWrapper(fooWrapper) {} virtual void run() { m_fooWrapper.anotherFunc(); } private: FooWrapper& m_fooWrapper; }; TEST(ScriptPluginWrapperTest, CallsFromMultipleThreads) { // FooWithMutex inherits the abstract Foo and adds // mutex lock/unlock on each function. FooWithMutex fooWithMutex; FooWrapper fooWrapper( &fooWithMutex ); ThreadThatCallsFooFunc1 thread1(fooWrapper); ThreadThatCallsFooFunc2 thread2(fooWrapper); fooWithMutex.m_mutex.lock(); thread1.start(); // Should block YieldThread(); ASSERT_FALSE( thread1.isFinished() ); thread2.start(); // Should finish immediately YieldThread(); ASSERT_TRUE( thread2.isFinished() ); fooWithMutex.m_mutex.unlock(); YieldThread(); EXPECT_TRUE( thread1.isFinished() ); }



Intel Threadchecker .

Si recuerdo correctamente, la herramienta verifica tu código para posibles carreras de datos teóricamente posibles. El punto es que no necesita ejecutar su código para verificar si es correcto o no.