ffi.spec.ts 1.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445
  1. import koffi from 'koffi'
  2. import { describe, expect, it, vi } from 'vitest'
  3. import {
  4. Win32Error,
  5. allocPtrSlot,
  6. decodePtr,
  7. isNullPtr,
  8. throwLastError,
  9. } from '../src/index.ts'
  10. import { PROCESS_INFORMATION_SIZE, STARTUPINFOW_SIZE } from '../src/abi.ts'
  11. import { PROCESS_INFORMATION, STARTUPINFOW, errorText } from '../src/ffi.ts'
  12. import type { NativePtr, Win32ProcessBindings } from '../src/index.ts'
  13. describe('shared Win32 process ABI', () => {
  14. it('matches the verified x64 structure sizes', () => {
  15. expect(STARTUPINFOW.size).toBe(STARTUPINFOW_SIZE)
  16. expect(PROCESS_INFORMATION.size).toBe(PROCESS_INFORMATION_SIZE)
  17. })
  18. it('handles NULL pointer out-parameters', () => {
  19. const slot = allocPtrSlot()
  20. expect(decodePtr(slot)).toBeNull()
  21. expect(isNullPtr(0n as NativePtr)).toBe(true)
  22. expect(isNullPtr(1n as NativePtr)).toBe(false)
  23. })
  24. it('formats and throws the exact Win32 error', () => {
  25. const api = {
  26. getLastError: vi.fn(() => 5),
  27. formatMessageW: vi.fn((_flags, _source, _id, _language, buffer: Buffer) => {
  28. buffer.write('access denied', 'utf16le')
  29. return 'access denied'.length
  30. }),
  31. } as unknown as Win32ProcessBindings
  32. expect(errorText(api, 5)).toBe('access denied')
  33. expect(() => throwLastError(api, 'Probe')).toThrow(Win32Error)
  34. expect(new Win32Error('CloseHandle', 6).message).toBe('CloseHandle failed (Win32 6)')
  35. })
  36. it('decodes a pointer stored by Koffi', () => {
  37. const slot = allocPtrSlot()
  38. koffi.encode(slot, koffi.pointer('void'), 42n)
  39. expect(decodePtr(slot)).toBe(42n)
  40. })
  41. })