First steps in Colemak#
Thu Apr 15 13:29:26 2021
Topics:
This is more of a placeholder than anything else, but I decided at the
weekend that it was time to learn to touchtype, and further, that it
would make sense to switch to Colemak at the same time. I don't yet
know if it actually will prove correct, it's too early too say.
I'm using Colemak Academy and have
achieved 97% on the home keys at an astonishingly speedy 18 words per
minute, but the top and bottom rows are still strangers to me. And
only after trying to use Emacs did I learn that the Colemak layout
rebound Caps lock (which I bind to Ctrl) as Backspace).
What's up, D?#
Fri Apr 30 16:27:22 2021
One of the nice things about side projects (as opposed to the ones I
get paid for) is not having to be accountable to anybody for timely
delivery, and the freedom to head off on tangents or side
investigations. I have recently picked up NixWRT again, but I regret
to inform you that "off piste" is where I am right now.
Backstory: monit is not a great service monitor for my needs, mostly
because it isn't the parent process of the service daemons it starts,
so can't tell when they exit without polling/pid files etc - which is
slow and unreliable. So I decided that the world does not have enough
init replacements already, and because of the risk that anyone else
might otherwise find it useful I decided to
write it in Fennel. Then a little while later, I decided to do something else for about five months, and now
I have no mental context of what I was doing.
So why not start again? This time it's called
upd and although it shares some ideas
with swarm, I am attempting to use tests to drive more of its design.
I started by sketching out the shape of a plausible service monitor
for a pppoe daemon.
You can see that the state logic is complicated and it has many
collaborators, so I install mock versions of them by
setting entries in the package.loaded
table
which require
checks before loading a file. The most complicated bit is probably the fake event loop: the test setup provides an array my-events
of functions which may update state, replace mocks etc, perform assertions etc, and and the mock event loop runs them one by one. This lets me write my
first test, to see that the daemon is started when the interface is
present. A followup
refactor
replaces the longwinded calls to tset package.loaded
with more intention-revealing names mock
and mocks
.
The second test checks that we observe an exponential backoff when the
process fails to start. We don't have any visibility into the actual
backoff state in the system under test, so have to observe from the
outside. First we stop the process from an event function, and then poll
the process state repeatedly while incrementing a counter until it
restarts. 0e966459
This is convoluted and might suggest that we should make that state
visible. But it also prompted me to try making backoff state be a
property of the process monitor itself, not the overall system under
test - suppose we want to write a script that watches two or more
processes? The next few commits
Of course, making the backoff state a property of the process has the
side effect of making it visible. The next test is that the process is
stopped if the underlying link is lost, and it's actually quite handy
to be able to reset the backoff interval in the test setup:
0350c8aed6a
Something was still bothering me here, though. In order to move the backoff testing from the script to the process, I'd done
- (when (and (not (process.running? pppd))
- (nil? pppd.backoff-until))
+ (when (not (process.running? pppd))
(pppd:backoff))
This is logically correct, as pppd:backoff
does nothing anyway if
pppd.backoff-until
is nil. But it's really quite non-obvious. What
we actually want to say here is "if the process died, back off" - so
let's say it! Add a
process.died?
method and rewrite the test to use it.
The last interesting change in this sequence was triggered by looking at the
event loop, when I realised that we don't anywhere use the value of the
event that comes back from next-event
- we wait for something to
happen, but then we discern what happened by testing various bits of
state. To say the same thing again in fewer words: we're don't receive
events, we wait for state changes. So let's change the code to
use better names
What's the conclusion? Is there a conclusion? A few thoughts -
-
for the TDD purists, it wasn't "real" TDD because I wrote quite a
lot of implementation code first instead of writing the test first.
-
by listening to the pain of testing I was able to improve the
micro-level design in a few aspects: I am happier with the result than
with the initial code
-
by having the tests I was able to make those changes while being
reasonably confident that I hadn't broken anything
-
the next thing on my list (which is in progress already) is writing
the service monitor for a hardware ethernet device. When it's in
slightly better shape I will write another post full of netlink and
qemu trivia, but until then, goodbye.