Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experiment Commands #3: Add command for exp gc with selectable flags #269

Merged
merged 7 commits into from
Apr 14, 2021

Conversation

rogermparent
Copy link
Contributor

@rogermparent rogermparent commented Apr 13, 2021

Based on #268. Part of #229, which is part of #239

This PR adds a single VSCode Command for dvc gc, which invokes a QuickPick menu that allows the user to select any of --all-branches, --all-tags, --all-commits, and --queue.

gc-quickpick

  • Any items the user selects are translated to their respective flags and passed to dvc gc in one command.
  • The command always includes dvc exp gc -f -w. This is also the command run when the quickpick is properly submitted with no selected options.
    • -f to skip the CLI warning (if anything, we'll implement our own)
    • -w because it is effectively always enabled, and it only exists so dvc exp gc with no options won't do anything.
  • If the QuickPick menu is dismissed with esc, no command is run
  • The stdout or stderr of a command will be reported through the showInformationMessage and showErrorMessage functions, respectively.

gc-info-boxes

We could handle this more gracefully by intercepting known errors and displaying more concise messages, but I think that can be considered its own issue that could best be handled with a more unified way of command output reporting.

@rogermparent rogermparent changed the base branch from master to run-all-queued-command April 13, 2021 20:43
@rogermparent rogermparent requested a review from mattseddon April 13, 2021 21:29
@rogermparent rogermparent changed the title Add command for dvc gc with selectable flags Experiment Commands #3: Add command for dvc gc with selectable flags Apr 13, 2021
@rogermparent rogermparent changed the title Experiment Commands #3: Add command for dvc gc with selectable flags Experiment Commands #3: Add command for exp gc with selectable flags Apr 13, 2021
@rogermparent rogermparent self-assigned this Apr 13, 2021
Copy link
Contributor

@mattseddon mattseddon left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few questions on this one

EXPERIMENT_GC = 'exp gc -f -w'
}

export enum DvcGcPreserveFlag {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[N] no need for Dvc

DvcGcQuickPickItem,
experimentGcCommand,
queueExperimentCommand
} from './index'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[N] use '.'

@@ -9,7 +9,15 @@ export enum Commands {
PUSH = 'push',
STATUS = 'status --show-json',
QUEUE_EXPERIMENT = 'exp run --queue',
RUN_ALL_EXPERIMENTS = 'exp run --run-all'
RUN_ALL_EXPERIMENTS = 'exp run --run-all',
EXPERIMENT_GC = 'exp gc -f -w'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[I] Hoist with other experiment commands


test('it invokes a QuickPick with snapshotted options', async () => {
await experimentGcCommand(exampleConfig)
expect(mockedShowQuickPick.mock.calls).toMatchInlineSnapshot(`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Q] What is the benefit of using a snapshot here?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Q] I see all the other tests have mockedShowQuickPick.mockResolvedValue how does this one get it's resolved value?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you moved the implementation to a constant like so

export const gcQuickPickItems = [
      {
        label: 'All Branches',
        detail: 'Preserve Experiments derived from all Git branches',
        flag: DvcGcPreserveFlag.ALL_BRANCHES
      },
      {
        label: 'All Tags',
        detail: 'Preserve Experiments derived from all Git tags',
        flag: DvcGcPreserveFlag.ALL_TAGS
      },
      {
        label: 'All Commits',
        detail: 'Preserve Experiments derived from all Git commits',
        flag: DvcGcPreserveFlag.ALL_COMMITS
      },
      {
        label: 'Queued Experiments',
        detail: 'Preserve all queued Experiments',
        flag: DvcGcPreserveFlag.QUEUED
      }
    ]

export const gcQuickPickOptions = {
  canPickMany: true,
  placeHolder: 'Select which Experiments to preserve'
}

then you could import that into the test and check that is what is being passed to show quick pick.

You could also use gcQuickPickItems.slice(1,3) for the next test down giving this implementation in the test:

  it('should invoke a QuickPick with the gcQuickPickItems options', async () => {
    await experimentGcCommand(exampleConfig)
    expect(mockedShowQuickPick.mock.calls).toEqual([
      [gcQuickPickItems, gcQuickPickOptions]
    ])
  })

  it('should executes the proper command given a mocked selection', async () => {
    mockedShowQuickPick.mockResolvedValue(gcQuickPickItems.slice(1, 3))

    await experimentGcCommand(exampleConfig)

    expect(mockedExecPromise).toBeCalledWith(
      'dvc exp gc -f -w --all-tags --all-commits',
      {
        cwd: exampleConfig.workspaceRoot
      }
    )
  })

and this in the code:

export const gcQuickPickOptions = {
  canPickMany: true,
  placeHolder: 'Select which Experiments to preserve'
}

export const experimentGcCommand = async (config: Config) => {
  const quickPickResult = (await window.showQuickPick<DvcGcQuickPickItem>(
    gcQuickPickItems,
    gcQuickPickOptions
  )) as undefined | DvcGcQuickPickItem[]

  if (quickPickResult) {
    try {
      const stdout = await experimentGarbageCollect(
        {
          cwd: config.workspaceRoot,
          cliPath: config.dvcPath
        },
        quickPickResult.map(({ flag }) => flag)
      )
      window.showInformationMessage(stdout)
    } catch (e) {
      window.showErrorMessage(e.stderr || e.message)
    }
  }
}

I still think that quick pick functionality should be outsourced from this file.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Q] What is the benefit of using a snapshot here?

This test acts as check that allows us to refactor the experiment GC command in any way and expect the same shape, and with a snapshot a dev can compare changed outputs and deliberately accept the changes with a jest -u.

[Q] I see all the other tests have mockedShowQuickPick.mockResolvedValue how does this one get it's resolved value?

This test doesn't set a resolved value because the resolved value of showQuickPick doesn't matter in this test, just the arguments passed to it. Explicitly defining the mock implementation to return something like undefined would be more explicit, but functionally the exact same.

If you moved the implementation to a constant like so [...] then you could import that into the test and check that is what is being passed to show quick pick.

While that is doable, it would add unnecessary exports and overhead while not adding any benefit over using a snapshot.
Directly comparing an exported argument constant would require the test to be changed around for most implementation changes, while the snapshot holds true regardless of where the QuickPick options come from and how they're build.

I still think that quick pick functionality should be outsourced from this file.

At the risk of sounding like I'm pulling a "no, you!", I think index would be best as a module that handles how the dvc commands interface to VSCode with stuff like quickpicks, and the functions that run the commands be in their own file.

I do agree that the cli dir needs a bit of restructuring, as it's become a bit of a big ball of mud over time.

}

export const experimentGcCommand = async (config: Config) => {
const quickPickResult = await window.showQuickPick<DvcGcQuickPickItem>(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[Q] do you think this would be better placed in another file? This is probably also a better candidate for an integration test.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do you think this would be better placed in another file?

Maybe these defined vscode callbacks and the registerCommands function could find a better home in something like a module named commands.

This is probably also a better candidate for an integration test.

I disagree, mocks test this functionality perfectly fine. The integration tests are already quite heavy and often flaky, we should only use them when they're really necessary. Jest tests also provide coverage and are much more easily integrated into all forms of CI.

* Change execCommand to resolve to stdout and simplify its consumers

* runDvcCommand => runCommand

* Reorganize command enum to distinguish exp commands with a prefix

* DvcGc => Gc
@rogermparent rogermparent merged commit 5b93e3f into run-all-queued-command Apr 14, 2021
@rogermparent rogermparent deleted the gc-commands branch April 14, 2021 16:28
rogermparent added a commit that referenced this pull request Apr 14, 2021
* Add "Run Queued Experiments" command

* Add test for "runQueuedExperiments"

* Experiment Commands #3: Add command for `exp gc` with selectable flags (#269)

* Add command for GC

* Change gc to exp gc, and add leading `--` to enum flags

* Add GC command tests and export GC QuickPickItem interface

* Rename exp gc enum entry and reader command

* Change gcExperiments to experimentGarbageCollect

* Replace test() with it() for consistency

* Experiment Commands #4: Addressing comments from 1-3 (#271)

* Change execCommand to resolve to stdout and simplify its consumers

* runDvcCommand => runCommand

* Reorganize command enum to distinguish exp commands with a prefix

* DvcGc => Gc
rogermparent added a commit that referenced this pull request Apr 14, 2021
* Add a "Queue Experiments" command

No tests yet, but there is info message functionality which seems to work well.

* Try to make a test for the new queue command

* Re-add failing test

* Move individual test file into index test file

* Use commands enum for queue experiment

* Rename `queue_experiment` enum to follow new allcaps convention

* Experiments Commands #2: Add "Run All Queued Experiments" Command (#268)

* Add "Run Queued Experiments" command

* Add test for "runQueuedExperiments"

* Experiment Commands #3: Add command for `exp gc` with selectable flags (#269)

* Add command for GC

* Change gc to exp gc, and add leading `--` to enum flags

* Add GC command tests and export GC QuickPickItem interface

* Rename exp gc enum entry and reader command

* Change gcExperiments to experimentGarbageCollect

* Replace test() with it() for consistency

* Experiment Commands #4: Addressing comments from 1-3 (#271)

* Change execCommand to resolve to stdout and simplify its consumers

* runDvcCommand => runCommand

* Reorganize command enum to distinguish exp commands with a prefix

* DvcGc => Gc

Co-authored-by: mattseddon <37993418+mattseddon@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants