Skip to content

Latest commit

 

History

History
273 lines (158 loc) · 12.2 KB

File metadata and controls

273 lines (158 loc) · 12.2 KB

DiffEngineTray

DiffEngineTray sits in the Windows tray. For supported snapshot testing libraries, it monitors pending changes in snapshots, and provides a mechanism for accepting those changes. It is intended as an alternative to using the clipboard as an approval mechanism.

NuGet

Installation

dotnet tool install -g DiffEngineTray

Running

Run diffenginetray in a console to start the app.

UI

Grouping

Moves, deletes and pending snapshots are grouped by the containing solution. In the above, the files exist in DiffEngine, so they are grouped under it. The per-group headers act on that group only: clicking one solution's "Pending Snapshots" does not accept another's.

Moves

"Pending Moves" will accept the changes to file3 and file4.

Clicking "file3" or "file4" will accept the changes to file3 or file4 respectively. The drop down will expose extra actions for that change.

Deletes

A test can produce multiple resulting snapshots. If the accepted versions has a different number of snapshots to the current test run, then some of those snapshots need to be deleted. The delete functionality in the tray tool handles this scenario.

"Pending Deletes" will delete file1 and file2.

Clicking "file1" or "file2" will delete file1 or file2 respectively. The drop down will expose extra actions for that change.

A delete is withdrawn when a later test run verifies against its file again, since the file is then in use rather than stale: DiffRunner.SettleDelete(file) drops the pending delete and leaves the file alone. It reaches a tray that owns the inline queue, which is the usual arrangement since the tray starts at login. A pending move onto the file withdraws the delete as well, whichever process owns the queue: a run that produced a received file for it is a run that verified against it.

Pending snapshots

Inline snapshots are reviewed in DiffEngineViewer rather than in a diff tool. The queue of them lives in whichever process claims the loopback port first, and stays there for as long as that process runs. With no tray, that is the viewer. With one, it is normally the tray, because the tray starts at login and the viewer only starts when a snapshot fails.

When the tray holds it, the window becomes disposable. A viewer that is closed, killed or crashes takes nothing pending with it, and the tray opens a new one on the same queue. A snapshot arriving with no window open starts one.

The viewer installed with the tray also reads documents: PDF, Word, Excel and PowerPoint files as text, as their pages drawn, or both, and SVGs and maps drawn beside their text. The copy bundled in the DiffEngine package does not. On a machine with the tray installed, test runs resolve the tray's copy ahead of the bundled one, so pending documents open in a viewer that can read them.

"Pending Snapshots" accepts all of them. Clicking one accepts that one, and its drop down offers discard, opening the viewer on it, and opening the source file. A snapshot that failed to apply is marked with ! and stays pending, so it can be retried once whatever blocked it is out of the way.

Exiting the tray writes any still-pending inline snapshots back to disk, under the source project's obj/VerifyInline/, where accept tooling such as Verify.Terminal still finds them. Logging off or shutting down does the same, and a snapshot that arrives once the session is ending is refused, so the test run stages it itself. A kill or a crash skips that, and loses the queue as it loses pending moves and deletes; re-run the tests.

Accept all

"Accept all" will accept all pending moves, deletes and inline snapshots. Snapshots whose target frameworks disagree about the content are skipped rather than picked between; resolve those in the viewer.

The deletes it carries out are the ones that were pending when it began. A delete can be the last copy of a snapshot that is moving inline, so the deletes are held back, and the tray says so, when a snapshot could not be written or when a viewer that owns the queue did not answer. A delete of a file that an accepted move has written is left pending rather than carried out, and stays that way through later "Accept all"s: it is marked ~ in the menu, with the reason. It goes when it is accepted on its own, or discarded and raised by a later run. A test run raising it again does not release it unless the file has changed since the move wrote it.

A long queue takes a while to accept. An open DiffEngineViewer window shows how far it has got, with each snapshot leaving the list as it lands.

Locked files

If accepting a move fails because the files are locked by another process (for example the snapshot is open in Microsoft Word), a prompt is shown listing the locked files and the locking processes:

  • "Ignore" leaves the move pending so it can be accepted later.
  • "Kill [process] and accept" kills the locking processes and accepts the move.
  • "Kill and accept all pending" kills the locking processes and accepts all pending moves, killing any other locking processes without further prompts.
  • "Always kill" kills the locking processes and accepts the move. The choice is stored in settings, so future locked files are killed without prompting. It can be toggled in the Options dialog.

Discard

Discard will clear all currently tracked items. It is the same discard as the per item menu and as the one in DiffEngineViewer: a move loses its received file, a pending delete keeps its file and is untracked, and every pending inline snapshot is dropped.

Purge verified files

Prompts for a directory, and then recursively deletes all *.verified.* in that directory.

Debug view

Everything currently tracked, as text: every field of every pending move, delete and snapshot, plus which process owns the inline queue.

The menu shows each pending item reduced to what fits on a line, a name and a few actions. This is the rest of it, for when the interesting part is a path, an argument list, or the process a diff tool was launched as. "Copy" puts the whole report on the clipboard, which is what to attach to an issue.

Nothing pushes at the window, so it shows the moment it was read. "Refresh" takes a newer reading.

Options

Run at startup

Runs DiffEngineTray at system startup.

Open on left

By default, when a diff is opened, the temp file will be on the left and the target file will be on the right. To invert this, select "Open on left".

Max instances to launch

Control the max instances to launch setting.

Always kill locking processes

When accepting a move with locked files, kill the locking processes without prompting.

Discard all HotKey

Registers a system wide HotKey to discard pending:

  • Deletes
  • Moves
  • Inline snapshots

Accept all HotKey

Registers a system wide HotKey to accept pending:

  • Deletes
  • Moves
  • Inline snapshots (conflicted ones are skipped)

Accept all open HotKey

Registers a system wide HotKey to accept pending:

  • Deletes
  • Moves that are currently open in a diff tool. A pair whose tool is the viewer counts: it is drawn as a row in the one window every pending pair shares, rather than in a process of its own
  • Inline snapshots, all of which are open by definition: the viewer only stays running while it has something to show

To limit impact on system resources, the default max concurrent open tool instances is limited to 5.

Accept all open HotKey allows the current batch of open diffs to be accepted.

Opting out of tracking

Pending moves and deletes are sent to a running tray by the DiffEngine library inside the test process. That tracking is separate from launching a diff tool: every exit from DiffRunner.Launch adds the move, DiffRunner.Disabled included, so turning diff off does not turn it off.

To opt a process out, set an environment variable DiffEngine_TrayDisabled with the value true, or in code:

DiffRunner.TrayDisabled = true;

The case it exists for is a test suite that needs the launch to happen but does not want what it produces collected: for example a suite asserting on the files a snapshot library stages, where each run would otherwise leave the tray a pending entry pointing at a throwaway directory. A move with no tray falls through to whatever owns the inline queue, and goes nowhere when nothing does.

Currently supported in

Payloads

Add pending move

{
"Type":"Move",
"Temp":"theTempFilePath",
"Target":"theTargetFilePath",
"CanKill":true,
"Exe":"theExePath",
"Arguments":"TheArguments",
"ProcessId":1000
}

snippet source | anchor

Add pending delete

{
"Type":"Delete",
"File":"theFilePath"
}

snippet source | anchor

Derived from another file

A move or a delete of a file that was derived from a document names the temp file of the pending move it was derived from, in a Source property that is absent otherwise:

{
"Type":"Move",
"Temp":"theTempFilePath",
"Target":"theTargetFilePath",
"CanKill":true,
"Exe":"theExePath",
"Arguments":"TheArguments",
"ProcessId":1000,
"Source":"theSourceTempFilePath"
}

snippet source | anchor

{
"Type":"Delete",
"File":"theFilePath",
"Source":"theSourceTempFilePath"
}

snippet source | anchor

The tray tracks such a file as it tracks any other, so it is listed in the menu and taken by an accept-all. The property is only passed on to the viewer, which is what folds the file beneath its document. A tray older than the property ignores it.

Logging Directory

Beside the installed tool, so it moves with the target framework the tray is built for:

%UserProfile%\.dotnet\tools\.store\diffenginetray\{VERSION}\diffenginetray\{VERSION}\tools\net10.0\any\logs

For example:

C:\Users\simon\.dotnet\tools\.store\diffenginetray\20.0.0\diffenginetray\20.0.0\tools\net10.0\any\logs

The menu's "Open logs" opens it without any of that.