Unverified Commit c6f2ee49 authored by Matt Sturgeon's avatar Matt Sturgeon
Browse files

keymap-drawer.tests: use simple in-tree fixtures and semantic assertions

- Simple in-tree test fixtures, instead of a snapshot of a real config.
- Semantic XML/YAML assertions instead of byte-for-byte comparisons.
parent 91fc68a2
Loading
Loading
Loading
Loading
+9 −0
Original line number Diff line number Diff line
draw_config:
  key_w: 50
  key_h: 50
  split_gap: 20
  glyphs: {}

parse_config:
  preprocess: true
+168 −78
Original line number Diff line number Diff line
{
  lib,
  fetchFromGitHub,
  runCommand,
  stdenv,
  keymap-drawer,
  runCommandWith,
  testers,

  keymap-drawer,
  libxml2,
  xmlstarlet,
  yamllint,
  yq-go,
}:
let
  runKeymapDrawer =
    name:
    runCommand "keymap-drawer-${name}" {
      nativeBuildInputs = [ keymap-drawer ];
    };

  MattSturgeon-example = fetchFromGitHub {
    owner = "MattSturgeon";
    repo = "glove80-config";
    rev = "d55267dd26593037256b35a5d6ebba0f75541da5";
    hash = "sha256-MV6cNpgHBuaGvpu2aR1aBNMpwPnDqOSbGf+2ykxocP4=";
    nonConeMode = true;
    sparseCheckout = [
      "config"
      "img"
    ];
    { name, ... }@args:
    runCommandWith {
      name = "${keymap-drawer.name}-${name}";
      derivationArgs = removeAttrs args [ "name" ] // {
        nativeBuildInputs = [ keymap-drawer ] ++ args.nativeBuildInputs or [ ];
      };

  # MattSturgeon's example requires MDI icons
  mdi = fetchFromGitHub {
    owner = "Templarian";
    repo = "MaterialDesign-SVG";
    tag = "v7.4.47";
    hash = "sha256-NoSSRT1ID38MT70IZ+7h/gMVCNsjNs3A2RX6ePGwuQ0=";
    };
in
{
  dump-config = runKeymapDrawer "dump-config" ''
  dump-config =
    runKeymapDrawer
      {
        name = "dump-config";
        nativeBuildInputs = [
          yamllint
        ];
      }
      /* bash */ ''
        keymap dump-config --output "$out"

        if [ ! -s "$out" ]; then
@@ -44,46 +35,145 @@ in
          exit 1
        fi

    ${lib.getExe yamllint} --strict --config-data relaxed "$out"
        yamllint --strict --config-data relaxed "$out"
      '';

  parse-zmk = testers.testEqualContents {
    assertion = "keymap parse --zmk-keymap produces expected YAML";
    expected = "${MattSturgeon-example}/img/glove80.yaml";
    actual = runKeymapDrawer "parse" ''
  invalid-keymap = testers.testBuildFailure (
    runKeymapDrawer
      {
        name = "invalid-keymap";
      }
      /* bash */ ''
        keymap \
        --config ${MattSturgeon-example}/config/keymap_drawer.yaml \
        parse --zmk-keymap ${MattSturgeon-example}/config/glove80.keymap \
          --config ${./config.yaml} \
          parse --zmk-keymap ${./invalid.keymap} \
          --output "$out"
    '';
    checkMetadata = stdenv.buildPlatform.isLinux;
  };
      ''
  );

  draw = testers.testEqualContents {
    assertion = "keymap draw produces expected SVG";
    expected = "${MattSturgeon-example}/img/glove80.svg";
    actual = runKeymapDrawer "draw" ''
      ${lib.optionalString stdenv.buildPlatform.isLinux ''
        export XDG_CACHE_HOME="$PWD/cache"
        glyphs="$XDG_CACHE_HOME/keymap-drawer/glyphs"
      ''}
      ${lib.optionalString stdenv.buildPlatform.isDarwin ''
        export HOME="$PWD/home"
        glyphs="$HOME/Library/Caches/keymap-drawer/glyphs"
      ''}
      mkdir -p "$glyphs"

      # Unpack MDI icons into the cache
      for file in ${mdi}/svg/*
      do
        ln -s "$file" "$glyphs/mdi:$(basename "$file")"
      done
  parse-zmk =
    runKeymapDrawer
      {
        name = "parse";
        nativeBuildInputs = [
          yamllint
          yq-go
        ];
      }
      /* bash */ ''
        keymap \
          --config ${./config.yaml} \
          parse --zmk-keymap ${./minimal.keymap} \
          --output "$out"

        yamllint --strict --config-data relaxed "$out" || {
          >&2 echo "Malformed YAML"
          exit 1
        }

        layer_count=$(yq '.layers | length' "$out")
        (( layer_count == 2 )) || {
          >&2 echo "Expected 2 layers, found $layer_count"
          exit 1
        }

        default_layer=$(yq --exit-status '.layers.default | flatten' "$out") || {
          >&2 echo "Expected default layer"
          exit 1
        }

        fn_layer=$(yq --exit-status '.layers.fn | flatten' "$out") || {
          >&2 echo "Expected fn layer"
          exit 1
        }

        [ "$default_layer" = '[ESC, A, B, SPACE, C, D]' ] || {
          >&2 echo "Incorrect default layer: $default_layer"
          exit 1
        }

        [ "$fn_layer" = '[F1, F2, F3, F4, F5, F6]' ] || {
          >&2 echo "Incorrect fn layer: $fn_layer"
          exit 1
        }

        yq --exit-status '.. | select(. == "ESC")' "$out" >/dev/null || {
          >&2 echo "Expected 'ESC' key"
          exit 1
        }

        yq --exit-status '.. | select(. == "SPACE")' "$out" >/dev/null || {
          >&2 echo "Expected 'SPACE' key"
          exit 1
        }

        yq --exit-status '.. | select(. == "F1")' "$out" >/dev/null || {
          >&2 echo "Expected 'F1' key"
          exit 1
        }
      '';

  draw =
    runKeymapDrawer
      {
        name = "draw";
        nativeBuildInputs = [
          xmlstarlet
          libxml2
        ];
      }
      /* bash */ ''
        keymap \
        --config ${MattSturgeon-example}/config/keymap_drawer.yaml \
        draw ${MattSturgeon-example}/img/glove80.yaml \
          --config ${./config.yaml} \
          draw ${./minimal.yaml} \
          --output "$out"

        ns="http://www.w3.org/2000/svg"

        # Basic XML validity
        xmllint --noout "$out" || {
          >&2 echo "Malformed XML"
          exit 1
        }

        # Root element is <svg>
        xmlstarlet sel -N s="$ns" -t -v "name(/s:svg)" "$out" | grep -qx svg || {
          >&2 echo "Expected <svg> root element"
          exit 1
        }

        # Assert two layers exist
        layer_count=$(xmlstarlet sel -N s="$ns" -t -v 'count(//s:g[contains(@class, "layer-")])' "$out")
        (( layer_count == 2 )) || {
          >&2 echo "Expected exactly 2 layers, found $layer_count"
          exit 1
        }

        for layer in default fn; do
          # layer-id class
          xmlstarlet sel -N s="$ns" -t -v 'count(//s:g[@class="layer-'"$layer"'"])' "$out" | grep -qx 1 || {
            >&2 echo "Missing layer-$layer class"
            exit 1
          }

          # text label
          xmlstarlet sel -N s="$ns" -t -v '//s:text[@class="label" and text()="'"$layer"':"]' "$out" | grep -q "$layer": || {
            >&2 echo "Missing $layer layer text label"
            exit 1
          }
        done

        key_count=$(xmlstarlet sel -N s="$ns" -t -v 'count(//s:g[contains(@class, "keypos-")])' "$out")
        (( key_count == 12 )) || {
          >&2 echo "Expected 12 keys total (6 per layer), found $key_count"
          exit 1
        }

        for key in ESC A B SPACE C D F1 F2 F3 F4 F5 F6; do
          xmlstarlet sel -N s="$ns" -t -v "//s:text[@class='key tap' and text()='$key']" "$out" | grep -q "$key" || {
            >&2 echo "Expected key '$key'"
            exit 1
          }
        done
      '';
    checkMetadata = stdenv.buildPlatform.isLinux;
  };
}
+11 −0
Original line number Diff line number Diff line
/ {
  keymap {
    compatible = "zmk,keymap";

    broken_layer {
      bindings = <
        &kp ESC   &kp A
        &kp SPACE   // <-- missing closing + inconsistent row
    };
  };
};
+19 −0
Original line number Diff line number Diff line
/ {
  keymap {
    compatible = "zmk,keymap";

    default_layer {
      bindings = <
        &kp ESC   &kp A   &kp B
        &kp SPACE &kp C   &kp D
      >;
    };

    fn_layer {
      bindings = <
        &kp F1  &kp F2  &kp F3
        &kp F4  &kp F5  &kp F6
      >;
    };
  };
};
+13 −0
Original line number Diff line number Diff line
layout:
  ortho_layout:
    rows: 2
    columns: 3
layers:
  default:
    - [ESC, A, B]
    - [SPACE, C, D]

  fn:
    - [F1, F2, F3]
    - [F4, F5, F6]