Add simple mocking mechanism.

This commit is contained in:
Alejandro Cabeza Romero 2024-08-27 18:52:53 +02:00
parent 3e68f916f6
commit 6501a611ba
No known key found for this signature in database
GPG Key ID: DA3D14AE478030FD
1 changed files with 48 additions and 0 deletions

48
tests/utils/mock.nim Normal file
View File

@ -0,0 +1,48 @@
# Sourced from https://forum.nim-lang.org/t/9255#60617
import posix
type Instr {.union.} = object
bytes: array[8, byte]
value: uint64
proc mockImpl*(target, replacement: pointer) =
# YOLO who needs alignment
#doAssert (cast[ByteAddress](target) and ByteAddress(0x07)) == 0
var page = cast[pointer](cast[ByteAddress](target) and (not 0xfff))
doAssert mprotect(page, 4096, PROT_WRITE or PROT_EXEC) == 0
let rel = cast[ByteAddress](replacement) - cast[ByteAddress](target) - 5
var instr = Instr(
bytes: [
0xe9.byte,
(rel shr 0).byte,
(rel shr 8).byte,
(rel shr 16).byte,
(rel shr 24).byte,
0,
0,
0,
]
)
cast[ptr uint64](target)[] = instr.value
doAssert mprotect(page, 4096, PROT_EXEC) == 0
# Note: Requires manual cleanup
# Usage Example:
# proc helloWorld(): string =
# "Hello, World!"
#
# echo helloWorld() # "Hello, World!"
#
# let backup = helloWorld
# mock(helloWorld):
# proc mockedHellWorld(): string =
# "Mocked Hello, World!"
# mockedHellWorld
#
# echo helloWorld() # "Mocked Hello, World!"
#
# mock(helloWorld):
# backup
template mock*(target, replacement: untyped): untyped =
mockImpl(cast[pointer](target), cast[pointer](replacement))