plugins library

What a flutter3d plugin has to do, as checks it runs against itself.

The other half of this package. flutter3d_conformance.dart holds a backend to what flutter3d_hardware cannot say in a signature; this holds a plugin to what flutter3d_plugin_api cannot: that it says what it is, that switching it off takes back everything it added, that a step it runs gives one answer, that it runs where it says it runs, and that it costs what it says it costs, and that a substance it brings is one the engine can trust. Decision 20 of tasks/0.9-plugins.md.

import 'package:flutter3d_conformance/plugins.dart';

void main() => runPluginConformance(
  WindPlugin.new,
  harness: PluginHarness(
    setUp: (loop) => loop
      ..snapshots.add(world.snapshotPart)
      ..addSystem('world', LoopPhase.physics, world.step),
  ),
);

The 8 plugin checks

  • plugin.manifest — the id is well formed, the plugin API version installs on this engine, every permission is one the engine knows, a budget reads, the manifest is the same written out and read back, and the plugin installs: a view plugin that adds a step system is refused here, with the loop's own sentence.
  • plugin.switch — switched off at a step boundary, the loop's phases, systems, declared events, fields and subsystems are exactly those of a loop without it; switched back on, exactly those with it; both journalled at the step they were made.
  • plugin.names — every phase, system, event, field and subsystem it adds to the loop is in its namespace, <namespace>.<name>; the engine's own packages are not held to one.
  • plugin.determinism — every step run twice from one state by the loop's DeterminismCheck, which names the system that diverged; and two fresh runs publish the same events and leave the same world, step by step.
  • plugin.backends — installed and stepped on every backend the manifest names (on none and all four when it names none), and switched off with a reason on one it does not.
  • plugin.budget — no step publishes more events than the manifest's budget allows, and the median cost of a step with the plugin, less without it, is within its stepMicroseconds.
  • plugin.materials — every physical material it adds to the engine's MaterialCatalog is under its own id, names a source for each group, says a liquid's or a gas's density and viscosity and keeps every number in SI within what anything real has (PhysicalMaterial.problems); every pair it measures has one of its own in it and a source; and all of it is gone when it is switched off. A plugin that brings none passes.
  • plugin.render — installed on the software backend with the harness's registries, whether the plugin places nodes in the frame: one that does names render.nodes in its outcome's requires, and the render suite's checks on cpu are then part of its badge.

The badge

flutter3d conformant@1.0: checkPluginConformance passes every check of suite 1.0 on every declared backend, with a world given to determinism and a budget declared and kept, on the plugin API version in its manifest. earnsBadge answers it and badgeFor names it. A declined check — no world, no budget, assertions off — is never green and keeps the badge away, because a check that was not asked has not been passed.

Render conformance counts for a plugin that draws. A plugin that places nodes in the frame (plugin.render) earns the badge only with the render checks it requires passed on the software backend, cpu (runPluginRenderConformance of flutter3d_conformance_render), and a plugin of any kind keeps it only while no render check it ran failed on any backend. The two suites write one report: each keeps the other's outcomes in <package>.conformance.json and replaces its own, so the badge in it is judged on both, whichever ran last.

The badge names the suite it was earned against. A check added later joins a new suite version, never an old one, so a plugin that earned conformant@1.0 keeps it when this package grows; it earns conformant@1.1 by passing the larger suite.

![flutter3d conformant@1.0](https://img.shields.io/badge/flutter3d-conformant%401.0-2ea44f)

Classes

PluginBudget
What a plugin promises to cost, read from its manifest's budget.
PluginCheckOutcome
What one check found, on one backend where the check is per backend.
PluginHarness
What a plugin's conformance run is given besides the plugin: the world it acts on, the registries it asks for, and the application around it.

Constants

conformanceReportFormat → const FormatSpec
The conformance report's format: what conformanceReport writes and a catalogue or a CI job reads.
conformanceSuites → const Map<String, List<String>>
Every suite version, with the checks it is made of.
conformanceSuiteVersion → const String
The suite this package's checks make up: the version a badge earned with them names.
pluginCheckNames → const List<String>
The checks' names, in the order they are reported.
renderBadgeBackend → const String
The backend a render check a badge requires is passed on: the software rasteriser, which every machine has and CI runs on every change.

Functions

badgeFor(List<PluginCheckOutcome> outcomes) → String?
The badge outcomes earn, as it is written — conformant@1.0 — for the newest suite they pass; null when they pass none.
checkPluginConformance(Flutter3dPlugin create(), {PluginHarness harness = const PluginHarness()}) → List<PluginCheckOutcome>
Runs every plugin check against the plugin create makes, and returns what each found — one outcome per check, and one per backend for the backends check.
conformanceReport(String plugin, List<PluginCheckOutcome> outcomes) → Map<String, Object?>
outcomes as a JSON document in the format envelope: the suite, the badge, and each outcome.
earnsBadge(List<PluginCheckOutcome> outcomes, {String suite = conformanceSuiteVersion}) → bool
Whether outcomes earn the badge of suite: every check of that suite ran, every one passed and none declined; every check an outcome PluginCheckOutcome.requires — the render checks of a plugin that draws — passed on the software backend, cpu, and was not declined there; and no render check failed on any backend.
mergedOutcomes(String plugin, List<PluginCheckOutcome> ran, {required bool isOurs(PluginCheckOutcome outcome)}) → List<PluginCheckOutcome>
ran in place of the outcomes of isOurs in the report already in the package, if there is one: what a suite writes, so the plugin suite and the render suite, each in a test file of its own, leave one report holding both, judged by earnsBadge on both.
recordRenderOutcomes(String plugin, List<PluginCheckOutcome> ran) → void
Writes ran, the render checks' outcomes of plugin on the backends they ran on, into the package's <package>.conformance.json, beside the plugin suite's and replacing the render outcomes of those backends, with the badge judged again on all of them. What runPluginRenderConformance calls once its checks are done.
runPluginConformance(Flutter3dPlugin create(), {PluginHarness harness = const PluginHarness()}) → void
Registers the plugin checks as tests: one per check, and one per backend for the backends check, named "plugin.backends on webgl".