Skip to content

Instantly share code, notes, and snippets.

@devyn
Created July 8, 2014 10:48
Show Gist options
  • Save devyn/904a1888c2b53544facc to your computer and use it in GitHub Desktop.
Save devyn/904a1888c2b53544facc to your computer and use it in GitHub Desktop.

The specification namespace

Contains helpers for specifying behavior at the very lowest level of Nucleus.

Should only be available if the environment is configured in an implementation-specific way (for example, a command line switch or a compile time option).

specification assert

Call-pattern:

  1. Caller.
  2. A label (the name).
  3. An execution (the test).

Clones the locals of the caller to the test, as well as the pair objects within locals, then stages the test with the name as the response. (Most likely the execution is pristine, anyway, so this doesn't really matter.)

If specification signal receives a label matching the name, the test is considered to have passed and the caller is restaged with its own, original (non-cloned) locals. Otherwise, if the implementation determines that no further action could possibly occur (i.e., not waiting on any I/O or anything like that and nothing in the queue), the test is considered to have failed.

The implementation SHOULD log the result of the test, most likely including the name.

Rationale for the 2nd argument being a label: It's the easiest comparison to define. Object identity might not be the simplest starting point in some languages. It's also useful for logging.

Rationale for defining pass/fail in terms of 'no further action': Many operations in Nucleus don't restage the caller in the event of some kind of failure. This is the simplest way to test that behavior.

Rationale for cloning locals and its pair objects: Isolation, basically. We shouldn't need to add teardown logic to the tests before we can assume that the machine is safe enough to do so!

Rationale for returning the caller's locals: So that we don't need to rely on something like implementation void. Not so sure about this one.

Example

specification assert[] "label compare foo with foo" {
  infrastructure label compare[] foo foo
  [specification signal "label compare foo with foo"]
}

If the compare returns, the signal will be staged, so the test will pass. The compare doesn't have to be able to take the result of signal, since signal doesn't ever return.

specification refute

Call-pattern:

  1. Caller.
  2. A label (the name).
  3. An execution (the test).

Same behavior as specification assert, except instead of considering a signal a pass, it considers a signal a failure. If no signal is received before the implementation determines no further action could occur, the test is a pass.

Rationale: Makes it very easy to assert that something will not return. For example, a failed lookup.

Example

specification refute[] "label compare foo with bar" {
  infrastructure label compare[] foo bar
  [specification signal "label compare foo with bar"]
}

If the compare doesn't return (which it shouldn't), the signal will never be staged, and that's considered a pass.

specification signal

Call-pattern:

  1. A label (the name).

Signals a pass to the specification assert invocation of the same name, or a fail to the specification refute invocation of the same name.

Doesn't take a caller or return, so make sure you're ready to exit your test before you call this.

Rationale: Super simple, and allows nested tests, which is necessary when dealing with so little available to us, at least at first.

A larger example

specification assert[] "infrastructure compare [] with []" {
  infrastructure compare[] [] []
  [specification signal "infrastructure compare [] with []"]
}

specification refute[] "infrastructure compare empty with empty" {
  infrastructure compare[] [infrastructure empty[]]
                           [infrastructure empty[]]

  [specification signal "infrastructure compare empty with empty"]
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment