Module: Terminalwire::V2::Conformance

Defined in:
lib/terminalwire/v2/conformance.rb

Overview

Loads the language-neutral conformance corpus and resolves its typed sentinels into native Ruby values. The Go and Elixir runners do the equivalent. This is what lets one corpus validate every implementation.

Two file shapes live in the corpus:

* simple vector tables (negotiate/roundtrip/golden/validate/flow) — YAML/JSON,
with `$bin` base64 + `bytes_hex` sentinels;
* "tapes" (session) — S-expressions (see Sexp): recorded client<->server
interactions. Sexp is used for the tapes because it is trivial and
unambiguous to parse in every language and reads like a transcript.

Defined Under Namespace

Modules: Sexp

Class Method Summary collapse

Class Method Details

.hex_to_bytes(hex) ⇒ Object

"a1 74 ff" -> binary string



67
68
69
# File 'lib/terminalwire/v2/conformance.rb', line 67

def hex_to_bytes(hex)
  hex.split.map { |byte| Integer(byte, 16) }.pack("C*")
end

.load(category) ⇒ Object

Load every vector file in a category, dispatching by extension, and return a flat array of cases with sentinels resolved to native values. Fails LOUDLY when the corpus is absent rather than silently running zero cases.



35
36
37
38
39
40
41
42
43
44
45
46
47
48
# File 'lib/terminalwire/v2/conformance.rb', line 35

def load(category)
  unless vectors_dir.directory?
    raise "conformance corpus not found at #{vectors_dir} — set TERMINALWIRE_CORPUS " \
          "to the corpus directory (it ships in terminalwire/protocol). Without it " \
          "the corpus specs would silently run zero cases."
  end
  Dir.glob(vectors_dir.join(category, "*.{yml,yaml,json,sexp}")).sort.flat_map do |path|
    case File.extname(path)
    when ".sexp" then Sexp.load(File.read(path))      # tapes (bin already resolved)
    when ".json" then resolve(JSON.parse(File.read(path)))
    else resolve(YAML.safe_load_file(path))
    end
  end
end

.resolve(value) ⇒ Object

Recursively resolve { "$bin" => base64 } sentinels into binary strings.



51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/terminalwire/v2/conformance.rb', line 51

def resolve(value)
  case value
  when Hash
    if value.size == 1 && value.key?("$bin")
      Base64.decode64(value.fetch("$bin")).b
    else
      value.transform_values { |v| resolve(v) }
    end
  when Array
    value.map { |v| resolve(v) }
  else
    value
  end
end

.rootObject



22
23
24
25
26
# File 'lib/terminalwire/v2/conformance.rb', line 22

def root
  Pathname.new(ENV.fetch("TERMINALWIRE_CORPUS") do
    File.expand_path("../../../conformance", __dir__)
  end)
end

.vectors_dirObject



28
29
30
# File 'lib/terminalwire/v2/conformance.rb', line 28

def vectors_dir
  root.join("vectors")
end