It was in C and I didn't do any language extending/precompiling, but I had interfaces for everything related to I/O, execution actors, randomness, etc.
On target hardware everything used TCP/UDP/real disk, pthreads, normal rand sources, etc. In simulation everything used virtual networking, a single simulation thread stepping all event loops, test-specified random seeds for reproducibility, etc.
It is completely invaluable. I can concisely write completely deterministic system tests that will execute tens of thousands of lines of code. I can fuzz test actor scheduling, I/O problems like dropped packets/msgs, and everything you can think of. I can run the entire test suite in valgrind and other nice tools. I can put a big machine in a corner of the office to fuzz test the suite for weeks on end and email me when a a test fails and tell me exactly which random seed to use to reproduce the failure myself within minutes. I can debug the entire simulation perfectly in GDB.
I've barely begun to describe how great it is to have, how many bugs these tests have caught or what a reliable regression test suite one can build. It doesn't replace testing on target - I do that extensively as well. Big system scenario tests don't replace smaller module and unit tests - I do those too. But deterministic simulation testing saved my sanity. Don't hesitate to evaluate this approach if you're doing something similar.
(1) Here are the slides I used: http://www.slideshare.net/FoundationDB/deterministic-simulat...
(2) Here is a (significantly out of date) white paper of ours which covers some of the same territory: https://foundationdb.com/key-value-store/white-papers/testin...
What's your standpoint on formal verification? Did you guys think about it and reject it?