Conformance-Testing a Custom Adapter¶
You have written a custom adapter — KpaStruct / KpaList / KpaPrimitive
(and, for mutation, RebuildableKpaStruct / RebuildableKpaList) over some tree type kPointer
doesn't ship a module for. This page shows how to point the portable algorithm/resolve.json and
algorithm/mutate.json fixtures at it, using the published kpointer-conformance module.
What the fixtures actually exercise¶
resolve and mutate are generic functions in kpointer-adapter, written once against the sealed
KpaElement hierarchy — they are not reimplemented per adapter. So running the algorithm
fixtures against your adapter isn't testing a from-scratch reimplementation of resolve/mutate; it's
testing whether your KpaStruct / KpaList / KpaPrimitive (and factory, if mutable)
correctly report their contents — keys, indices, absent-vs-error, and rebuild-on-mutate — so
that the shared algorithm behaves correctly on top of them. That is exactly the contract
Verifying your adapter points at.
Add the dependency¶
kpointer-conformance is published on Maven Central under the same group as the rest of kPointer:
[versions]
kpointer = "0.2.0"
[libraries]
kpointer-conformance = { module = "com.commonsware.kpointer:kpointer-conformance", version.ref = "kpointer" }
Declare it as a test dependency in the module holding your adapter:
The module gives you two small, reusable pieces for this: ConformanceCapabilities
(to skip cases your adapter deliberately doesn't support) and ConformanceResult (a Passed /
Failed / Skipped sealed type for uniform reporting). The fixture data — the actual
algorithm/resolve.json and algorithm/mutate.json files — is not embedded in the artifact; it
ships separately in the conformance distribution (or you can read it
directly from a checkout of the conformance/ directory). Download or vendor whichever fixture
files you want to run.
Bridge the fixture document shape to your tree¶
Each algorithm/resolve.json / algorithm/mutate.json case carries its input document (and, for
resolve, its expected result) as an adapter-neutral, discriminated JSON shape — see
Fixture Schemas for the full grammar:
{ "type": "struct", "fields": { "name": { "type": "string", "value": "Alice" } } }
{ "type": "list", "elements": [ { "type": "long", "value": 1 } ] }
Write a small converter from that shape into your own tree type — the same role
SimpleDocElement.toKpaElement() plays for the reference JSON adapter in kpointer-conformance's
own test suite:
import kotlinx.serialization.json.*
fun JsonElement.toMyTree(): MyNode = when (val type = jsonObject["type"]!!.jsonPrimitive.content) {
"string" -> MyNode.leaf(jsonObject["value"]!!.jsonPrimitive.content)
"long" -> MyNode.leaf(jsonObject["value"]!!.jsonPrimitive.long)
"struct" -> MyNode.struct(jsonObject["fields"]!!.jsonObject.mapValues { (_, v) -> v.toMyTree() })
"list" -> MyNode.list(jsonObject["elements"]!!.jsonArray.map { it.toMyTree() })
// … boolean, double, null
else -> error("unhandled fixture element type: $type")
}
You need the same kind of function to compare your adapter's resolved KpaElement back against a
fixture's expect.element — mirroring SimpleDocElement.matches().
Run the fixtures¶
For each case in the parsed algorithm/resolve.json, build your document, resolve the pointer with
the real get operator from kpointer-adapter (the same function every other adapter uses), and
compare:
val root: MyKpaStruct = caseDocumentJson.toMyTree().asKpaStruct()
val pointer = KPointer.from(caseJson["pointer"]!!.jsonPrimitive.content)
val result = root[pointer] // com.commonsware.kpointer.adapter.get
when (val expect = caseJson["expect"]!!.jsonObject) {
// "type": "element" -> assert result != null && expect.element.matches(result)
// "type": "absent" -> assert result == null
// "type": "error" -> assert the KpaAccessError thrown and its canonicalErrorCode()
}
algorithm/mutate.json follows the same shape, driving your RebuildableKpaStruct /
RebuildableKpaList and the mutate { … } DSL instead of get.
A green run across both files, for every case whose requiresCapability (if any) your
ConformanceCapabilities profile declares, is the same bar the built-in JSON/YAML/KSoup adapters
are held to.