Skip to content

Commit ae824fd

Browse files
authored
Add minimum version to test fixture upgrader (#2427)
- First step of #2282 ## Checklist - [-] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [x] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [x] I have not broken the cheatsheet
1 parent 945d5aa commit ae824fd

File tree

7 files changed

+100
-23
lines changed

7 files changed

+100
-23
lines changed

docs/contributing/test-case-recorder.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -79,9 +79,19 @@ Recorded tests will automatically be picked up and run with the normal tests.
7979

8080
To clean up the formatting of all of the yaml test cases, run `pnpm transform-recorded-tests`
8181

82-
### Upgrading fixtures
82+
### Canonicalizing fixtures
8383

84-
To upgrade all the test fixtures to the latest command version, run the command `pnpm transform-recorded-tests --upgrade`. This command should be idempotent.
84+
To upgrade test fixtures to their canonical, latest form, run the command `pnpm transform-recorded-tests --canonicalize <paths>`. This command should be idempotent.
85+
86+
### Partially upgrading fixtures
87+
88+
We periodically upgrade test case fixtures to use the version of our command payload from one year ago. To do so, proceed as follows:
89+
90+
1. Look at the blame of the big switch statement in the `upgradeCommand` function in [`canonicalizeAndValidateCommand`](../../packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts). You can do this on the web [here](https://github.com/cursorless-dev/cursorless/blame/main/packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts)
91+
1. Find the newest `case` branch that is at least one year old
92+
1. Look at the version number that is the guard of that case branch; the minimum number should be that + 1
93+
1. Run `pnpm transform-recorded-tests --upgrade --minimum-version 5`, where 5 is the minimum version number you found
94+
1. Open a PR with the changes
8595

8696
### Custom transformation
8797

packages/common/src/types/command/command.types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export type CommandLatest = Command & {
1616
version: typeof LATEST_VERSION;
1717
};
1818

19+
export type CommandVersion = Command["version"];
20+
1921
export type Command =
2022
| CommandV0
2123
| CommandV1

packages/cursorless-engine/src/core/commandVersionUpgrades/canonicalizeAndValidateCommand.ts

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import {
22
ActionType,
33
Command,
44
CommandComplete,
5-
CommandLatest,
5+
CommandVersion,
66
EnforceUndefined,
77
LATEST_VERSION,
88
OutdatedExtensionError,
99
PartialTargetDescriptor,
1010
} from "@cursorless/common";
11+
import { produce } from "immer";
1112
import { getPartialTargetDescriptors } from "../../util/getPartialTargetDescriptors";
1213
import canonicalizeTargetsInPlace from "./canonicalizeTargetsInPlace";
1314
import { upgradeV0ToV1 } from "./upgradeV0ToV1";
@@ -17,7 +18,6 @@ import { upgradeV3ToV4 } from "./upgradeV3ToV4";
1718
import { upgradeV4ToV5 } from "./upgradeV4ToV5/upgradeV4ToV5";
1819
import { upgradeV5ToV6 } from "./upgradeV5ToV6";
1920
import { upgradeV6ToV7 } from "./upgradeV6ToV7";
20-
import { produce } from "immer";
2121

2222
/**
2323
* Given a command argument which comes from the client, normalize it so that it
@@ -29,7 +29,7 @@ import { produce } from "immer";
2929
export function canonicalizeAndValidateCommand(
3030
command: Command,
3131
): EnforceUndefined<CommandComplete> {
32-
const commandUpgraded = upgradeCommand(command);
32+
const commandUpgraded = upgradeCommand(command, LATEST_VERSION);
3333
const { action, usePrePhraseSnapshot = false, spokenForm } = commandUpgraded;
3434

3535
return {
@@ -45,12 +45,15 @@ export function canonicalizeAndValidateCommand(
4545
};
4646
}
4747

48-
function upgradeCommand(command: Command): CommandLatest {
48+
export function upgradeCommand<V extends CommandVersion>(
49+
command: Command,
50+
minimumVersion: V,
51+
): Command & { version: V } {
4952
if (command.version > LATEST_VERSION) {
5053
throw new OutdatedExtensionError();
5154
}
5255

53-
while (command.version < LATEST_VERSION) {
56+
while (command.version < minimumVersion) {
5457
switch (command.version) {
5558
case 0:
5659
command = upgradeV0ToV1(command);
@@ -80,11 +83,9 @@ function upgradeCommand(command: Command): CommandLatest {
8083
}
8184
}
8285

83-
if (command.version !== LATEST_VERSION) {
84-
throw new Error("Command is not latest version");
85-
}
86-
87-
return command;
86+
return command as Command & {
87+
version: V;
88+
};
8889
}
8990

9091
/**

packages/cursorless-engine/src/scripts/transformRecordedTests/checkMarks.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import { injectIde } from "../../singletons/ide.singleton";
44
import tokenGraphemeSplitter from "../../singletons/tokenGraphemeSplitter.singleton";
55
import { extractTargetKeys } from "../../testUtil/extractTargetKeys";
66
import { getPartialTargetDescriptors } from "../../util/getPartialTargetDescriptors";
7-
import { upgrade } from "./transformations/upgrade";
87
import assert from "assert";
8+
import { canonicalize } from "./transformations/canonicalize";
99

1010
export function checkMarks(originalFixture: TestCaseFixtureLegacy): undefined {
11-
const command = upgrade(originalFixture).command;
11+
const command = canonicalize(originalFixture).command;
1212

1313
injectIde(new FakeIDE());
1414
const graphemeSplitter = tokenGraphemeSplitter();

packages/cursorless-engine/src/scripts/transformRecordedTests/index.ts

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,39 @@
1-
import { getRecordedTestPaths } from "@cursorless/common";
1+
import {
2+
CommandVersion,
3+
LATEST_VERSION,
4+
TestCaseFixtureLegacy,
5+
getRecordedTestPaths,
6+
} from "@cursorless/common";
27
import { identity } from "./transformations/identity";
38
import { upgrade } from "./transformations/upgrade";
49
import { transformFile } from "./transformFile";
510
import { FixtureTransformation } from "./types";
611
import { upgradeDecorations } from "./upgradeDecorations";
712
import { checkMarks } from "./checkMarks";
13+
import { canonicalize } from "./transformations/canonicalize";
814

915
const AVAILABLE_TRANSFORMATIONS: Record<string, FixtureTransformation> = {
1016
upgrade,
17+
canonicalize,
1118
format: identity,
1219
["check-marks"]: checkMarks,
1320
custom: upgradeDecorations,
1421
};
1522

1623
async function main(args: string[]) {
17-
const [transformationName, paths] = args?.[0]?.startsWith("--")
18-
? [args[0].slice(2), args.slice(1)]
19-
: [null, args];
24+
const { transformationName, minimumVersion, paths } =
25+
parseCommandLineArguments(args);
2026

21-
const transformation =
27+
let transformation =
2228
transformationName == null
2329
? identity
2430
: AVAILABLE_TRANSFORMATIONS[transformationName];
2531

32+
if (transformation === upgrade && minimumVersion != null) {
33+
transformation = (originalFixture: TestCaseFixtureLegacy) =>
34+
upgrade(originalFixture, minimumVersion);
35+
}
36+
2637
if (transformation == null) {
2738
throw new Error(`Unknown transformation ${transformationName}`);
2839
}
@@ -47,4 +58,41 @@ async function main(args: string[]) {
4758
}
4859
}
4960

61+
function parseCommandLineArguments(args: string[]) {
62+
const paths = [];
63+
let transformationName: string | undefined = undefined;
64+
let minimumVersion: CommandVersion | undefined = undefined;
65+
for (let i = 0; i < args.length; i++) {
66+
const arg = args[i];
67+
if (arg.startsWith("--")) {
68+
const flagName = arg.slice(2);
69+
70+
if (flagName in AVAILABLE_TRANSFORMATIONS) {
71+
transformationName = flagName;
72+
continue;
73+
}
74+
75+
if (flagName === "minimum-version") {
76+
const minimumVersionUnchecked = parseInt(args[i + 1]);
77+
if (
78+
minimumVersionUnchecked < 0 ||
79+
minimumVersionUnchecked > LATEST_VERSION
80+
) {
81+
throw new Error(
82+
`Minimum version must be between 0 and ${LATEST_VERSION} inclusive`,
83+
);
84+
}
85+
minimumVersion = minimumVersionUnchecked as CommandVersion;
86+
i++;
87+
continue;
88+
}
89+
90+
throw new Error(`Unknown flag ${flagName}`);
91+
}
92+
paths.push(arg);
93+
}
94+
95+
return { transformationName, minimumVersion, paths };
96+
}
97+
5098
main(process.argv.slice(2));
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import { TestCaseFixture, TestCaseFixtureLegacy } from "@cursorless/common";
2+
import { canonicalizeAndValidateCommand } from "../../../core/commandVersionUpgrades/canonicalizeAndValidateCommand";
3+
4+
export function canonicalize(fixture: TestCaseFixtureLegacy): TestCaseFixture {
5+
return {
6+
...fixture,
7+
command: canonicalizeAndValidateCommand(fixture.command),
8+
};
9+
}
Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
1-
import { TestCaseFixture, TestCaseFixtureLegacy } from "@cursorless/common";
2-
import { canonicalizeAndValidateCommand } from "../../../core/commandVersionUpgrades/canonicalizeAndValidateCommand";
1+
import {
2+
CommandVersion,
3+
LATEST_VERSION,
4+
TestCaseFixtureLegacy,
5+
} from "@cursorless/common";
6+
import { upgradeCommand } from "../../../core/commandVersionUpgrades/canonicalizeAndValidateCommand";
37

4-
export function upgrade(fixture: TestCaseFixtureLegacy): TestCaseFixture {
8+
export function upgrade(
9+
fixture: TestCaseFixtureLegacy,
10+
minimumVersion: CommandVersion = LATEST_VERSION,
11+
): TestCaseFixtureLegacy {
512
return {
613
...fixture,
7-
command: canonicalizeAndValidateCommand(fixture.command),
14+
command: upgradeCommand(fixture.command, minimumVersion),
815
};
916
}

0 commit comments

Comments
 (0)