2009-01-22 18 views
5

Estoy escribiendo una infraestructura de prueba unitaria para una gran base de código Delphi. Me gustaría vincular llamadas a funciones puras en SysUtils.FileExists por ejemplo a un "MockSysUtils.FileExists" en su lugar.Reemplazar unidades de función

El compilador no aprecia la creación de una unidad SysUtils con la misma interfaz.

Lo que estoy pensando es conectar mi función de simulación en tiempo de ejecución. ¿Es esto posible hoy en día?

¿Alguna otra sugerencia?

Saludos,

Peter

Respuesta

9

Sustitución de una función en tiempo de ejecución es difícil, pero por lo general es técnicamente posible. "Todo" lo que necesita hacer es:

  • tomar la dirección de la función en cuestión
  • desmontar los primeros 5 bytes o menos (para comprobar si hay una instrucción RET - muy pequeñas rutinas pueden lindar otra rutina, evitando que desde que lo sustituya)
  • cambio de su protección de página (con VirtualProtect) tener permiso de escritura
  • reescribir los primeros 5 bytes con una instrucción rel32 JMP (es decir, E9 < offset a-su-func>)
  • implementar su la función de versión es normal, asegurándose de que tenga la Me argumentos y llamar a la convención como la función que se burla

Un enfoque más fácil sería vincularlo con una versión diferente de SysUtils.pas. Eso requerirá que también recompile todas las unidades en el RTL y VCL que dependen de SysUtils.pas, pero es probable que sea un poco más fácil que el enfoque de la función de instrumentación descrito anteriormente.

El enfoque más fácil es el nivel de idioma, donde o bien no confía directamente en SysUtils (y puede cambiar a un nivel superior) o modifica la declaración uses para referirse condicionalmente a una unidad diferente .

+0

"o modifica la declaración de usos para referirse condicionalmente a una unidad diferente." - me parece que esto debe ser lo más fácil para esto. Asegúrate de que tu unidad especial sea la última, y ​​luego puedes redefinir cualquier función. – mj2008

+0

Supone que la fuente de la unidad está disponible. Si es así, entonces es obvio; sin embargo, el que pregunta preguntó específicamente sobre el tiempo de ejecución. –

+0

Me gusta la ironía :) Pero tienes toda la razón. +1 – guerda

0

Gracias,

sí, sería bueno tener clase TSysUtils por ejemplo, en lugar de que podía heredar con mis MockSysUtils. Pero ese no es el caso y la base de códigos es enorme. Se reemplazará poco a poco, pero me pregunto si hay una solución de inicio rápido.

El primer enfoque está bien para una función quizás, pero no en este caso, supongo.

Iré por el segundo enfoque.

6

Puede hacerlo con MadCodeHook. Use la función HookCode, asígnele la dirección de la función que desea reemplazar y la dirección de la función a la que desea llamar. Le devolverá un puntero de función que puede usar para llamar al original y para desengancharlo después. En esencia, implementa los tres pasos intermedios de la descripción de Barry.

Creo que MadCodeHook es gratis para uso personal. Si buscas algo más libre que eso, puedes intentar encontrar an old version of the Tnt Unicode controls.Usó la misma técnica de enganche para inyectar soporte Unicode en algunos de los códigos de VCL. Necesitarás una versión anterior porque las versiones más recientes ya no son gratuitas. Busque la función OverwriteProcedure en TntSystem.pas, que es también donde encontrará ejemplos de cómo usarla.

El enganche de código es bueno porque no requiere la recompilación de RTL y VCL, y no implica la compilación condicional para controlar qué funciones están dentro del alcance. Puede enganchar el código de su procedimiento de configuración de prueba de unidad, y el código original nunca notará la diferencia. Pensará que está llamando a la función original FileExists (porque lo es), pero cuando llegue allí, saltará de inmediato a su versión burlada.

0

Esto está un poco fuera, pero aquí hay otra alternativa.

Cuando la construcción de las pruebas unitarias y su código base principal para ir con ella, podría grep todas las funciones que desea reemplazar y especificar la unidad a utilizar

En lugar de

fileexists(MyFilename); 

que podría grep fileexists y reemplazar con

MockTests.fileexists(MyFileName); 

Si lo hizo en tiempo de compilación (usando herramientas de construcción automatizados) que podría hacer fácilmente y que le proporcionará el GRE prueba flexibilidad. Simplemente podría tener un archivo de configuración que enumere todas las funciones que se reemplazarán.

1

También podría agregar una unidad que solo contenga las funciones que desea simular a la cláusula de usos de la unidad de prueba. Delphi siempre usará la función de la unidad que se encuentra en último lugar. Lamentablemente, esto requerirá que cambie la unidad que desea probar. unidad

Sus MockSysutils:

unit MockSysutils; 

interface 

function FileExists(...) ... 
... 
end. 

Su unidad, que desea probar:

unit UnitTotest; 

interface 

uses 
    Sysutils, 
    MockSysUtils; 

... 

    if FileExists(...) then 

FileExists ahora llamar a la versión de MockSysutils más que de sysutils.